Хочу вернуться к описанию контейнеров в "стиле STL".
Немножко я рассказал тут - http://18delphi.blogspot.com/2013/03/generic-generic.html
И тут:
http://18delphi.blogspot.com/2013/03/blog-post_4606.html
http://18delphi.blogspot.com/2013/04/iunknown.html
Но там я использовал динамические массивы.
А в РЕАЛЬНОМ своём "микро-STL" я использую прямую работу с памятью - GetMem/FreeMem.
Посему - хочу начать с того - как я работаю с памятью напрямую. Точнее то - как я инкапсулировал работу с памятью во всякие фасадные функции/объекты.
Первый пример - получение размера куска памяти. Немножко я рассказал уже тут - http://18delphi.blogspot.com/2013/04/getmem.html
Но теперь я хочу показать - как это нарисовано на UML. И как это инкапсулировано в отдельный модуль.
Итак.
Сначала - модель:
И код:
l3MemorySizeUtilsPrim.pas:
l3MemorySizeUtils.pas:
И тест:
Модель:
Код:
Дальше - я надеюсь - расскажу про инкапсуляцию GetMem/FreeMem/ReallocMem в "объект" Tl3Ptr.
Все исходники тут - https://sourceforge.net/p/rumtmarc/code-0/15/tree/trunk/Blogger/STL.1/
Немножко я рассказал тут - http://18delphi.blogspot.com/2013/03/generic-generic.html
И тут:
http://18delphi.blogspot.com/2013/03/blog-post_4606.html
http://18delphi.blogspot.com/2013/04/iunknown.html
Но там я использовал динамические массивы.
А в РЕАЛЬНОМ своём "микро-STL" я использую прямую работу с памятью - GetMem/FreeMem.
Посему - хочу начать с того - как я работаю с памятью напрямую. Точнее то - как я инкапсулировал работу с памятью во всякие фасадные функции/объекты.
Первый пример - получение размера куска памяти. Немножко я рассказал уже тут - http://18delphi.blogspot.com/2013/04/getmem.html
Но теперь я хочу показать - как это нарисовано на UML. И как это инкапсулировано в отдельный модуль.
Итак.
Сначала - модель:
И код:
l3MemorySizeUtilsPrim.pas:
unit l3MemorySizeUtilsPrim; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Библиотека "L3$Basic Concepts" // Модуль: "w:/common/components/rtl/L3/l3MemorySizeUtilsPrim.pas" // Родные Delphi интерфейсы (.pas) // Generated from UML model, root element: UtilityPack::Class Shared Delphi Требования к низкоуровневым библиотекам::L3$Basic Concepts::MemoryUtils::l3MemorySizeUtilsPrim // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ! Полностью генерируется с модели. Править руками - нельзя. ! interface type Tl3MemorySizeFunc = function (aPtr: Pointer): Integer; {$If not defined(XE)} function L3MemorySizeDelphi7(aPtr: Pointer): Integer; {* функция для получения размера куска памяти } {$IfEnd} //not XE {$If defined(XE)} function L3MemorySizeXE(aPtr: Pointer): Integer; {* функция для получения размера куска памяти } {$IfEnd} //XE implementation uses l3MemorySizeUtils ; // unit methods {$If not defined(XE)} function L3MemorySizeDelphi7(aPtr: Pointer): Integer; //#UC START# *51DAD8DC00B2_51DADE55035E_var* const cThisUsedFlag = 2; cPrevFreeFlag = 1; cFillerFlag = Integer($80000000); cFlags = cThisUsedFlag or cPrevFreeFlag or cFillerFlag; type PUsed = ^TUsed; TUsed = packed record sizeFlags: Integer; end;//TUsed //#UC END# *51DAD8DC00B2_51DADE55035E_var* begin //#UC START# *51DAD8DC00B2_51DADE55035E_impl* if (aPtr = nil) then Result := 0 else Result := PUsed(PMem(aPtr)-SizeOf(TUsed)).sizeFlags and not cFlags - sizeof(TUsed); // Result := (PLong(Long(aP) - 4)^ AND not cFlags) - 4; //#UC END# *51DAD8DC00B2_51DADE55035E_impl* end;//L3MemorySizeDelphi7 {$IfEnd} //not XE {$If defined(XE)} function L3MemorySizeXE(aPtr: Pointer): Integer; //#UC START# *51DADA9600F9_51DADE55035E_var* const {----------------------------Block type flags---------------------------} {The lower 3 bits in the dword header of small blocks (4 bits in medium and large blocks) are used as flags to indicate the state of the block} {Set if the block is not in use} IsFreeBlockFlag = 1; {Set if this is a medium block} IsMediumBlockFlag = 2; {Set if it is a medium block being used as a small block pool. Only valid if IsMediumBlockFlag is set.} IsSmallBlockPoolInUseFlag = 4; {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.} IsLargeBlockFlag = 4; {Is the medium block preceding this block available?} PreviousMediumBlockIsFreeFlag = 8; {Is this large block segmented? I.e. is it actually built up from more than one chunk allocated through VirtualAlloc? (Only used by large blocks.)} LargeBlockIsSegmented = 8; {The flags masks for small blocks} DropSmallFlagsMask = -8; ExtractSmallFlagsMask = 7; {The flags masks for medium and large blocks} DropMediumAndLargeFlagsMask = -16; ExtractMediumAndLargeFlagsMask = 15; {------------------------------Private types------------------------------} type {Move procedure type} TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt); {-----------------------Small block structures--------------------------} {Pointer to the header of a small block pool} PSmallBlockPoolHeader = ^TSmallBlockPoolHeader; {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).} PSmallBlockType = ^TSmallBlockType; TSmallBlockType = record {True = Block type is locked} BlockTypeLocked: Boolean; {Bitmap indicating which of the first 8 medium block groups contain blocks of a suitable size for a block pool.} AllowedGroupsForBlockPoolBitmap: Byte; {The block size for this block type} BlockSize: Word; {The minimum and optimal size of a small block pool for this block type} MinimumBlockPoolSize: Word; OptimalBlockPoolSize: Word; {The first partially free pool for the given small block. This field must be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.} NextPartiallyFreePool: PSmallBlockPoolHeader; {The last partially free pool for the small block type. This field must be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.} PreviousPartiallyFreePool: PSmallBlockPoolHeader; {The offset of the last block that was served sequentially. The field must be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.} NextSequentialFeedBlockAddress: Pointer; {The last block that can be served sequentially.} MaxSequentialFeedBlockAddress: Pointer; {The pool that is current being used to serve blocks in sequential order} CurrentSequentialFeedPool: PSmallBlockPoolHeader; {$ifdef UseCustomFixedSizeMoveRoutines} {The fixed size move procedure used to move data for this block size when it is upsized. When a block is downsized (which usually does not occur that often) the variable size move routine is used.} UpsizeMoveProcedure: TMoveProc; {$else} Reserved1: Pointer; {$endif} {$if SizeOf(Pointer) = 8} {Pad to 64 bytes for 64-bit} Reserved2: Pointer; {$ifend} end; {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).} TSmallBlockPoolHeader = record {BlockType} BlockType: PSmallBlockType; {$if SizeOf(Pointer) <> 8} {Align the next fields to the same fields in TSmallBlockType and pad this structure to 32 bytes for 32-bit} Reserved1: Cardinal; {$ifend} {The next and previous pool that has free blocks of this size. Do not change the position of these two fields: They must be at the same offsets as the fields in TSmallBlockType of the same name.} NextPartiallyFreePool: PSmallBlockPoolHeader; PreviousPartiallyFreePool: PSmallBlockPoolHeader; {Pointer to the first free block inside this pool. This field must be at the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.} FirstFreeBlock: Pointer; {The number of blocks allocated in this pool.} BlocksInUse: Cardinal; {Small block pool signature. Used by the leak checking mechanism to determine whether a medium block is a small block pool or a regular medium block.} SmallBlockPoolSignature: Cardinal; {The pool pointer and flags of the first block} FirstBlockPoolPointerAndFlags: NativeUInt; end; {Small block layout: At offset -SizeOf(Pointer) = Flags + address of the small block pool. At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block pool for the next small block. } {------------------------Medium block structures------------------------} {The medium block pool from which medium blocks are drawn. Size = 16 bytes for 32-bit and 32 bytes for 64-bit.} PMediumBlockPoolHeader = ^TMediumBlockPoolHeader; TMediumBlockPoolHeader = record {Points to the previous and next medium block pools. This circular linked list is used to track memory leaks on program shutdown.} PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader; NextMediumBlockPoolHeader: PMediumBlockPoolHeader; {Padding} Reserved1: NativeUInt; {The block size and flags of the first medium block in the block pool} FirstMediumBlockSizeAndFlags: NativeUInt; end; {Medium block layout: Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free) Offset: -SizeOf(Pointer) = This block size and flags Offset: 0 = User data / Previous Free Block (if this block is free) Offset: SizeOf(Pointer) = Next Free Block (if this block is free) Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free) Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags {A medium block that is unused} PMediumFreeBlock = ^TMediumFreeBlock; TMediumFreeBlock = record PreviousFreeBlock: PMediumFreeBlock; NextFreeBlock: PMediumFreeBlock; end; {-------------------------Large block structures------------------------} {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)} PLargeBlockHeader = ^TLargeBlockHeader; TLargeBlockHeader = record {Points to the previous and next large blocks. This circular linked list is used to track memory leaks on program shutdown.} PreviousLargeBlockHeader: PLargeBlockHeader; NextLargeBlockHeader: PLargeBlockHeader; {The user allocated size of the Large block} UserAllocatedSize: NativeUInt; {The size of this block plus the flags} BlockSizeAndFlags: NativeUInt; end; {---------------------------Private constants-----------------------------} const {The size of the block header in front of small and medium blocks} BlockHeaderSize = SizeOf(Pointer); {The size of a small block pool header} SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader); {The size of a medium block pool header} MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader); {The size of the header in front of Large blocks} LargeBlockHeaderSize = SizeOf(TLargeBlockHeader); (* {This memory manager} ThisMemoryManager: TMemoryManagerEx = ( GetMem: SysGetMem; FreeMem: SysFreeMem; ReallocMem: SysReallocMem; AllocMem: SysAllocMem; RegisterExpectedMemoryLeak: SysRegisterExpectedMemoryLeak; UnregisterExpectedMemoryLeak: SysUnregisterExpectedMemoryLeak); *) var lBlockHeader: Cardinal; LPSmallBlockType: PSmallBlockType; LOldAvailableSize: Cardinal; //#UC END# *51DADA9600F9_51DADE55035E_var* begin //#UC START# *51DADA9600F9_51DADE55035E_impl* if (aPtr = nil) then Result := 0 else begin {Get the block header: Is it actually a small block?} LBlockHeader := PNativeUInt(PByte(aPtr) - BlockHeaderSize)^; {Is it a small block that is in use?} if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then begin {----------------------------Small block------------------------------} {The block header is a pointer to the block pool: Get the block type} LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType; {Get the available size inside blocks of this type.} Result := LPSmallBlockType.BlockSize - BlockHeaderSize; end else begin {Is this a medium block or a large block?} if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then begin Result:= (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize; end else begin {Is this a valid large block?} if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then begin {-----------------------Large block------------------------------} {Get the block header} //LBlockHeader := PNativeUInt(PByte(aP) - BlockHeaderSize)^; {Subtract the overhead to determine the useable size in the large block.} Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize); end else begin {-----------------------Invalid block------------------------------} {Bad pointer: probably an attempt to reallocate a free memory block.} Result := 0; assert(false); end; end; end; end; //#UC END# *51DADA9600F9_51DADE55035E_impl* end;//L3MemorySizeXE {$IfEnd} //XE end.------
l3MemorySizeUtils.pas:
unit l3MemorySizeUtils; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Библиотека "L3$Basic Concepts" // Модуль: "w:/common/components/rtl/L3/l3MemorySizeUtils.pas" // Родные Delphi интерфейсы (.pas) // Generated from UML model, root element: UtilityPack::Class Shared Delphi Требования к низкоуровневым библиотекам::L3$Basic Concepts::MemoryUtils::l3MemorySizeUtils // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ! Полностью генерируется с модели. Править руками - нельзя. ! interface uses l3MemorySizeUtilsPrim ; type PMem = System.PANSIChar; {$If not defined(XE)} var l3MemorySize : Tl3MemorySizeFunc = L3MemorySizeDelphi7; {* функция для получения размера куска памяти} {$IfEnd} //not XE {$If defined(XE)} var l3MemorySize : Tl3MemorySizeFunc = L3MemorySizeXE; {* функция для получения размера куска памяти} {$IfEnd} //XE implementation end.
И тест:
Модель:
Код:
unit MemorySizeTest; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Библиотека "SandBoxTest" // Модуль: "w:/common/components/rtl/SandBox/MemorySizeTest.pas" // Родные Delphi интерфейсы (.pas) // Generated from UML model, root element: TestCase::Class Shared Delphi Sand Box::SandBoxTest::Memory::MemorySizeTest // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ! Полностью генерируется с модели. Править руками - нельзя. ! interface {$If defined(nsTest)} uses BaseTest ; {$IfEnd} //nsTest {$If defined(nsTest)} type TMemorySizeTest = class(TBaseTest) protected // overridden protected methods function GetFolder: AnsiString; override; {* Папка в которую входит тест } function GetModelElementGUID: AnsiString; override; {* Идентификатор элемента модели, который описывает тест } published // published methods procedure DoIt; end;//TMemorySizeTest {$IfEnd} //nsTest implementation {$If defined(nsTest)} uses l3MemorySizeUtils, SysUtils, TestFrameWork ; {$IfEnd} //nsTest {$If defined(nsTest)} // start class TMemorySizeTest procedure TMemorySizeTest.DoIt; //#UC START# *51DAE7030012_51DAE6E20300_var* var l_Index : Integer; l_Size : Integer; l_RealSize : Integer; l_P : Pointer; //#UC END# *51DAE7030012_51DAE6E20300_var* begin //#UC START# *51DAE7030012_51DAE6E20300_impl* for l_Index := 1 to 4 * 1024 do begin l_Size := l_Index * 2; System.GetMem(l_P, l_Size); try l_RealSize := l3MemorySize(l_P); Check(l_RealSize >= l_Size, Format('Выделяли %d. Выделилось %d.', [l_Size, l_RealSize])); finally System.FreeMem(l_P); end;//try..finally end;//form l_Index //#UC END# *51DAE7030012_51DAE6E20300_impl* end;//TMemorySizeTest.DoIt function TMemorySizeTest.GetFolder: AnsiString; {-} begin Result := 'Memory'; end;//TMemorySizeTest.GetFolder function TMemorySizeTest.GetModelElementGUID: AnsiString; {-} begin Result := '51DAE6E20300'; end;//TMemorySizeTest.GetModelElementGUID {$IfEnd} //nsTest initialization TestFramework.RegisterTest(TMemorySizeTest.Suite); end.
Дальше - я надеюсь - расскажу про инкапсуляцию GetMem/FreeMem/ReallocMem в "объект" Tl3Ptr.
Все исходники тут - https://sourceforge.net/p/rumtmarc/code-0/15/tree/trunk/Blogger/STL.1/
Комментариев нет:
Отправить комментарий