Быстрые спискиDelphi , Компоненты и Классы , СпискиБыстрые списки
Автор: Vitaly Sergienko { **** UBPFD *********** by delphibase.endimus.com **** >> Быстрые списки. Цепочка - односвязный список записей (record) фиксированной длины. Пул цепочек позволяет быстро манипулировать с множеством цепочек. Общая для всех цепочек конкретного пула память выделяется по мере необходимости страницами через VirtualAlloc, т.е. без обращения к менеджеру памяти (GetMem/FreeMem). Соответственно, освобождается память сразу всего пула. Удобно использовать цепочки в качестве рабочей памяти, когда нужно раскидать множество элементов по кучкам, а также при реализации графов ( списки соседей узла ), деревьев (списки детей узла). Большинство методов и функций написано на basm. Зависимости: System Автор: Alex Konshin, akonshin@earthlink.net, Boston, USA Copyright: http://home.earthlink.net/~akonshin/index.htm Дата: 30 декабря 2002 г. ***************************************************** } // (c) Alex Konshin mailto:alexk@mtgroup.ru 08 Feb 1999 // 21 jun 2000 - bugfix PutValue // all "uses" are removed unit ChainPools; interface //uses type PChainLink = ^TChainLink; PPChainLink = ^PChainLink; TChainLink = packed record FNext: PChainLink; FValue: LongInt; end; PPoolBuffer = ^TPoolBuffer; PPPoolBuffer = ^PPoolBuffer; TPoolBuffer = record FNextBuf: PPoolBuffer; end; TChainLinkCallBack = function(const Value: LongInt; const ALink: PChainLink; AParm: Pointer): Boolean; TScanChainCallBack = function(ALink: PChainLink; AParm: Pointer): Boolean of object; TChainPool = class protected FFirstBuffer: PPoolBuffer; FPLastBuffer: PPPoolBuffer; FRest: LongWord; FTop: PChar; FItemSize: Integer; function Allocate(const ASize: LongWord): Pointer; public FFreeChain: PChainLink; constructor Create(AItemSize: Integer = SizeOf(TChainLink)); destructor Destroy; override; procedure FreeBuffers; // извлекаем из цепочки первый элемент (освобождается) function GetValue(var AAnchor: PChainLink): LongInt; virtual; // извлекаем из цепочки первый элемент (отсоединяется от цепочки) function GetFirstLink(var AAnchor: PChainLink): PChainLink; virtual; // извлекаем из цепочки последний элемент (освобождается) function GetLastValue(var AAnchor: PChainLink): LongInt; virtual; // извлекаем из цепочки последний элемент (отсоединяется от цепочки) function GetLastLink(var AAnchor: PChainLink): PChainLink; virtual; // вставка в начало без проверки на уникальность procedure PutValue(const Value: LongInt; var AAnchor: PChainLink); virtual; // вставка в конец с проверкой, False - значение уже существует function AddValue(const Value: LongInt; var AAnchor: PChainLink): Boolean; virtual; // вставка перед звеном, на которое ACallBackFunc выдаст True function InsertValue(const Value: LongInt; var AAnchor: PChainLink; ACallBackFunc: TChainLinkCallBack; AParm: Pointer = nil): PChainLink; virtual; // вставка в цепочку в порядке возврастания Value function InsertSorted(const Value: LongInt; var AAnchor: PChainLink): PChainLink; virtual; // удаление указанного значения // function RemoveValue(const Value: LongInt; var AAnchor: PChainLink): Boolean; virtual; // удаление указанного звена function RemoveLink(const ALink: PChainLink; var AAnchor: PChainLink): Boolean; virtual; // создает новый элемент, непривязанный к какой-либо цепочке function NewLink: PChainLink; // virtual; // освобождение элемента - перенос в список FFreeChain (элемент не должен принадлежать какой-либо цепочке) procedure FreeLink(const ALink: PChainLink); virtual; // освобождение цепочки procedure FreeChain(var AAnchor: PChainLink); virtual; end; { TChainPool } // вставка элемента в начало цепочки(элемент не должен принадлежать какой-либо цепочке) procedure LinkTo(const ALink: PChainLink; var AAnchor: PChainLink); // вставка элемента в конец цепочки(элемент не должен принадлежать какой-либо цепочке) procedure Append(const ALink: PChainLink; var AAnchor: PChainLink); function MoveFirstToChain(var AFrom, ATo: PChainLink): PChainLink; function MoveChainLink(const Value: LongInt; var AAnchor, ATo: PChainLink): Boolean; function AppendChainLink(const Value: LongInt; var AFromChain, AToChain: PChainLink): Boolean; function LastChainLink(const AAnchor: PChainLink): PChainLink; // проверяем наличие указанного значения в цепочке function IsValueInChain(const Value: LongInt; AAnchor: PChainLink): Boolean; // ищем элемент (остается в цепочке) function FindValue(const Value: LongInt; AAnchor: PChainLink): PChainLink; function FindAndUnlink(var AAnchor: PChainLink; ACallBackFunc: TScanChainCallBack; AParm: Pointer = nil): PChainLink; function IndexOfValue(const Value: LongInt; AAnchor: PChainLink): LongInt; // ищем ChainLink, на который AScanChainFunc выдаст True function ScanChain(const AAnchor: PChainLink; AScanChainFunc: TScanChainCallBack; AParm: Pointer): PChainLink; function ChainLinkByIndex(AAnchor: PChainLink; AIndex: Integer): PChainLink; function ChainLinkCount(AAnchor: PChainLink): Integer; // сравнение цепочек, результат =0, <0, >0, abs(result) = индекс несовпавшего элемента function CompareChains(AFromChain, AToChain: PChainLink): LongInt; //============================================================= implementation const MEM_COMMIT = $1000; MEM_DECOMMIT = $4000; MEM_RELEASE = $8000; PAGE_READWRITE = 4; kernel = 'kernel32.dll'; type DWORD = LongInt; BOOL = LongBool; function VirtualAlloc(lpAddress: Pointer; dwSize, flAllocationType, flProtect: DWORD): Pointer; stdcall; external kernel name 'VirtualAlloc'; function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: DWORD): BOOL; stdcall; external kernel name 'VirtualFree'; //------------------------------------------------------------- // вставка элемента (элемент не должен принадлежать какой-либо цепочке) procedure LinkTo(const ALink: PChainLink; var AAnchor: PChainLink); begin ALink^.FNext := AAnchor; AAnchor := ALink; end; //------------------------------------------------------------- // вставка элемента в конец (элемент не должен принадлежать какой-либо цепочке) procedure Append(const ALink: PChainLink; var AAnchor: PChainLink); assembler; asm @@FindLast: mov ecx,edx mov edx,[edx] test edx,edx jnz @@FindLast mov [eax],edx mov [ecx],eax end; //------------------------------------------------------------- // перенос первого элемента в другую цепочку function MoveFirstToChain(var AFrom, ATo: PChainLink): PChainLink; assembler; asm mov ecx,[eax] test ecx,ecx jz @@Exit push edx mov edx,[ecx] mov [eax],edx pop edx mov eax,[edx] mov [ecx],eax mov [edx],ecx @@Exit: mov eax,ecx end; //------------------------------------------------------------- function MoveChainLink(const Value: LongInt; var AAnchor, ATo: PChainLink): Boolean; assembler; asm push edi mov edi,eax jmp @@Start @@Next: mov edx,eax @@Start: mov eax,[edx] test eax,eax jz @@Done cmp edi,[eax].TChainLink.FValue jne @@Next mov edi,[eax] mov [edx],edi mov edi,[ecx] mov [eax],edi mov [ecx],eax mov eax,1 @@Done: pop edi @@Exit: end; //------------------------------------------------------------- function AppendChainLink(const Value: LongInt; var AFromChain, AToChain: PChainLink): Boolean; assembler; asm push edi mov edi,eax jmp @@Start @@Next: mov edx,eax @@Start: mov eax,[edx] test eax,eax jz @@Done cmp edi,[eax].TChainLink.FValue jne @@Next mov edi,[eax] mov [edx],edi @@FindLast: mov edx,ecx mov ecx,[ecx] test ecx,ecx jnz @@FindLast mov edi,[edx] mov [eax],edi mov [edx],eax mov eax,1 @@Done: pop edi @@Exit: end; //------------------------------------------------------------- function LastChainLink(const AAnchor: PChainLink): PChainLink; assembler; asm mov edx,eax jmp @@Start @@Next: mov eax,edx mov edx,[edx] @@Start: test edx,edx jnz @@Next @@Exit: end; //------------------------------------------------------------- function ScanChain(const AAnchor: PChainLink; AScanChainFunc: TScanChainCallBack; AParm: Pointer): PChainLink; var pNext: PChainLink; begin pNext := AAnchor; while pNext <> nil do begin Result := pNext; pNext := pNext^.FNext; if AScanChainFunc(Result, AParm) then Exit; end; Result := nil; end; //------------------------------------------------------------- // сравнение цепочек, результат =0, <0, >0, abs(result) = индекс несовпавшего элемента function CompareChains(AFromChain, AToChain: PChainLink): LongInt; assembler; asm mov ecx,1 push esi mov esi,eax @@loop: inc ecx test esi,esi jz @@1 test edx,edx jz @@gt mov eax,[esi] sub eax,[edx] jz @@loop jc @@lt jmp @@gt @@1: test edx,edx jz @@exit @@lt: neg ecx @@gt: mov eax,ecx @@exit: pop esi end; //------------------------------------------------------------- function IsValueInChain(const Value: LongInt; AAnchor: PChainLink): Boolean; assembler; // проверяем наличие указанного значения в цепочке { while AAnchor<>nil do begin if AAnchor^.FValue=Value then begin Result := True; Exit; end; AAnchor := AAnchor^.FNext; end; Result := False; } asm test edx,edx jz @False @loop: cmp eax,TChainLink[edx].FValue je @True mov edx,[edx] test edx,edx jnz @loop @False: mov eax,0 jmp @Exit @True: mov eax,1 @Exit: end; //------------------------------------------------------------- function FindValue(const Value: LongInt; AAnchor: PChainLink): PChainLink; assembler; { Result := AAnchor; while (Result<>nil)and(Result^.FValue<>Value) do Result := Result^.FNext; } asm xchg eax,edx test eax,eax jz @Exit @loop: cmp edx,TChainLink[eax].FValue je @Exit mov eax,[eax] test eax,eax jnz @loop @Exit: end; //------------------------------------------------------------- function IndexOfValue(const Value: LongInt; AAnchor: PChainLink): LongInt; asm test edx,edx jz @NotFound mov ecx,eax xor eax,eax @loop: cmp ecx,TChainLink[edx].FValue je @Exit inc eax mov edx,[edx] test edx,edx jnz @loop @NotFound: mov eax,-1 @Exit: end; //------------------------------------------------------------- // Извлечение первого звена, для которого ACallBackFunc выдаст True // Внимание! Найденный элемент отсоединяется от цепочки. Для присоединения к другой цепочке используйте LinkTo, для освобождения памяти - TChainPool.FreeLink function FindAndUnlink(var AAnchor: PChainLink; ACallBackFunc: TScanChainCallBack; AParm: Pointer = nil): PChainLink; assembler; var pParm: Pointer; asm push edi push esi mov esi,eax // esi <= AAnchor mov eax,AParm mov pParm,eax mov edi,edx // edi <= ACallBackFunc @NextLink: mov eax,[esi] mov edx,pParm call edi test eax,eax jnz @Unlink mov esi,[esi] test esi,esi jnz @NextLink xor eax,eax // для всех получили отказ jmp @Exit @Unlink: mov eax,[esi] mov edx,[eax] mov [esi],edx mov dword ptr [eax],0 @Exit: pop esi pop edi end; //------------------------------------------------------------- function ChainLinkByIndex(AAnchor: PChainLink; AIndex: Integer): PChainLink; assembler; asm test eax,eax jz @Exit test edx,edx jz @Exit @Next: mov eax,[eax] test eax,eax jz @Exit dec edx jnz @Next @Exit: end; //------------------------------------------------------------- function ChainLinkCount(AAnchor: PChainLink): Integer; asm test eax,eax jz @Exit xor edx,edx @Next: mov eax,[eax] inc edx test eax,eax jnz @Next mov eax,edx @Exit: end; //==TChainPool=========================================================== constructor TChainPool.Create(AItemSize: Integer = SizeOf(TChainLink)); begin inherited Create; FPLastBuffer := @FFirstBuffer; FItemSize := AItemSize; end; //------------------------------------------------------------- destructor TChainPool.Destroy; begin FreeBuffers; inherited Destroy; end; //------------------------------------------------------------- procedure TChainPool.FreeBuffers; var pBuf: PPoolBuffer; begin while FFirstBuffer <> nil do begin pBuf := FFirstBuffer; FFirstBuffer := FFirstBuffer^.FNextBuf; VirtualFree(pBuf, 0, MEM_RELEASE); end; FPLastBuffer := @FFirstBuffer; FFreeChain := nil; FRest := 0; FTop := nil; end; //------------------------------------------------------------- function TChainPool.Allocate(const ASize: LongWord): Pointer; var nSize: LongInt; pBuf: PPoolBuffer; begin if FRest < ASize then begin nSize := (4095 + ASize + SizeOf(TPoolBuffer)) and (-4096); pBuf := PPoolBuffer(VirtualAlloc(nil, nSize, MEM_COMMIT, PAGE_READWRITE)); if pBuf = nil then GetMem(pBuf, not 0); // raise Exception.Create('ChainPools: Out of memory'); - SysUtils required FPLastBuffer^ := pBuf; FPLastBuffer := @(pBuf^.FNextBuf); FTop := PChar(pBuf) + SizeOf(TPoolBuffer); FRest := nSize - SizeOf(TPoolBuffer); end; Dec(FRest, ASize); Result := FTop; Inc(FTop, ASize); end; //------------------------------------------------------------- function TChainPool.AddValue(const Value: LongInt; var AAnchor: PChainLink): Boolean; assembler; asm push edi push esi xor esi,esi mov edi,eax mov eax,[ecx] test eax,eax jz @@New @@Compare: cmp edx,[eax].TChainLink.FValue je @@Done mov ecx,eax mov eax,[ecx] test eax,eax jnz @@Compare @@New: inc esi push edx mov eax,edi mov edi,ecx call TChainPool.NewLink mov ecx,edi @@Link: pop edx mov [eax].TChainLink.FValue,edx mov dword ptr [eax],0 mov [ecx],eax @@Done: mov eax,esi pop esi pop edi end; {TChainPool.AddValue} //------------------------------------------------------------- procedure TChainPool.PutValue(const Value: LongInt; var AAnchor: PChainLink); assembler; asm push edi mov edi,eax push edx push ecx call TChainPool.NewLink pop ecx pop edx mov TChainLink[eax].FValue,edx mov edx,[ecx] mov [eax],edx mov [ecx],eax @@Done: pop edi end; {TChainPool.PutValue} //------------------------------------------------------------- // Вставка перед звеном, на которое ACallBackFunc выдаст True // Будет предложено также вставить в конец - будет вызов ACallBackFunc с ALink=nil // TChainLinkCallBack = function ( const Value : LongInt; const ALink : PChainLink; AParm : Pointer ) : Boolean; function TChainPool.InsertValue(const Value: LongInt; var AAnchor: PChainLink; ACallBackFunc: TChainLinkCallBack; AParm: Pointer = nil): PChainLink; assembler; var pSelf: LongInt; asm push edi push esi mov pSelf,eax mov edi,edx // edi <= Value mov esi,ecx // esi <= AAnchor @NextLink: mov eax,edi // AValue mov edx,[esi] mov ecx,AParm call [ACallBackFunc] test eax,eax jnz @Insert mov esi,[esi] test esi,esi jnz @NextLink xor eax,eax // для всех получили отказ jmp @Exit @Insert: mov eax,pSelf call TChainPool.NewLink mov TChainLink[eax].FValue,edi mov edx,[esi] mov [eax],edx mov [esi],eax @Exit: pop esi pop edi end; {TChainPool.InsertValue} //------------------------------------------------------------- // CallBack для InsertSorted function ChainLinkAscending(const Value: LongInt; const ALink: PChainLink; AParm: Pointer): Boolean; begin Result := (Value < ALink.FValue); end; // вставка в цепочку в порядке возврастания Value function TChainPool.InsertSorted(const Value: LongInt; var AAnchor: PChainLink): PChainLink; begin Result := InsertValue(Value, AAnchor, ChainLinkAscending); end; //------------------------------------------------------------- // освобождение элемента - перенос в список FFreeChain (элемент не должен принадлежать какой-либо цепочке) procedure TChainPool.FreeLink(const ALink: PChainLink); begin ALink^.FNext := FFreeChain; FFreeChain := ALink; end; //------------------------------------------------------------- // создает новый элемент, непривязанный к какой-либо цепочке function TChainPool.NewLink: PChainLink; asm mov ecx,TChainPool[eax].FFreeChain test ecx,ecx jz @Allocate mov edx,[ecx] mov TChainPool[eax].FFreeChain,edx push ecx mov edx,TChainPool[eax].FItemSize mov eax,ecx mov ecx,0 call System.@FillChar pop eax jmp @Exit @Allocate: mov edx,TChainPool[eax].FItemSize call TChainPool.Allocate @Exit: end; //------------------------------------------------------------- function TChainPool.GetValue(var AAnchor: PChainLink): LongInt; assembler; asm mov ecx,[edx] test ecx,ecx jnz @@1 xor eax,eax jmp @@Exit @@1: push edi mov edi,[ecx] mov [edx],edi mov edi,[eax].TChainPool.FFreeChain mov [ecx],edi mov [eax].TChainPool.FFreeChain,ecx pop edi mov eax,[ecx].TChainLink.FValue @@Exit: end; //------------------------------------------------------------- function TChainPool.GetFirstLink(var AAnchor: PChainLink): PChainLink; assembler; asm mov eax,[edx] test eax,eax jz @Empty mov ecx,[eax] mov [edx],ecx mov dword ptr[eax],0 @Empty: end; //------------------------------------------------------------- function TChainPool.RemoveValue(const Value: LongInt; var AAnchor: PChainLink): Boolean; assembler; asm test eax,eax jz @@Exit push edi mov edi,eax jmp @@Start @@Next: mov ecx,eax @@Start: mov eax,[ecx] test eax,eax jz @@Done cmp edx,TChainLink[eax].FValue jne @@Next mov edx,[eax] // FNext mov [ecx],edx mov edx,TChainPool[edi].FFreeChain mov [eax],edx mov TChainPool[edi].FFreeChain,eax mov eax,1 @@Done: pop edi @@Exit: end; //------------------------------------------------------------- function TChainPool.RemoveLink(const ALink: PChainLink; var AAnchor: PChainLink): Boolean; assembler; asm test eax,eax jz @@Exit push edi mov edi,eax test edx,edx jz @@False test ecx,ecx jnz @@Start @@False: xor eax,eax jmp @@Done @@Next: mov ecx,eax @@Start: mov eax,[ecx] test eax,eax jz @@Done cmp edx,eax jne @@Next mov eax,[edx] mov [ecx],eax mov eax,[edi].TChainPool.FFreeChain mov [edx],eax mov [edi].TChainPool.FFreeChain,edx mov eax,1 @@Done: pop edi @@Exit: end; //------------------------------------------------------------- function TChainPool.GetLastValue(var AAnchor: PChainLink): LongInt; assembler; asm mov ecx,[edx] test ecx,ecx jnz @@Start xor eax,eax jmp @@Exit @@Next: mov edx,ecx mov ecx,[edx] @@Start: cmp dword ptr [ecx],0 jnz @@Next mov dword ptr [edx],0 mov edx,[eax].TChainPool.FFreeChain mov [ecx],edx mov [eax].TChainPool.FFreeChain,ecx mov eax,[ecx].TChainLink.FValue @@Exit: end; //------------------------------------------------------------- function TChainPool.GetLastLink(var AAnchor: PChainLink): PChainLink; assembler; asm mov eax,[edx] test eax,eax jz @Empty mov ecx,[eax] test ecx,ecx jz @Finish @@Next: mov edx,eax mov eax,ecx mov ecx,[eax] test ecx,ecx jnz @@Next @Finish: mov dword ptr [edx],0 @Empty: end; //------------------------------------------------------------- procedure TChainPool.FreeChain(var AAnchor: PChainLink); assembler; asm test eax,eax jz @@Exit test edx,edx jz @@Exit add eax,offset(TChainPool.FFreeChain) mov ecx,[edx] xchg ecx,[eax] mov eax,[edx] mov dword ptr[edx],0 test eax,eax jz @@Done @@Next: mov edx,eax mov eax,[edx] test eax,eax jnz @@Next @@Done: mov [edx],ecx @@Exit: end; end. Пример использования: program Test; {$APPTYPE CONSOLE} uses ChainPools; type PItem = ^TItem; TItem = packed record FNext: PItem; FValue: LongInt; FValue2: LongInt; end; // It is just a holder of method. // In real life use forms or other objects. TSomeClass = class function FindValue2(ALink: PChainLink; AParm: Pointer): Boolean; end; function TSomeClass.FindValue2(ALink: PChainLink; AParm: Pointer): Boolean; begin Result := PLongInt(AParm)^ = PItem(ALink)^.FValue2; end; var oChainPool: TChainPool; pFirst1: PItem; // pointer to the first item in chain 1 pFirst2: PItem; // pointer to the first item in chain 2 ptr: PItem; nValue: LongInt; i: Integer; oSomeObject: TSomeClass; begin oSomeObject := TSomeClass.Create; pFirst1 := nil; pFirst2 := nil; oChainPool := TChainPool.Create(SizeOf(TItem)); // Put some values in chain 1 for i := 0 to 99 do oChainPool.AddValue(i, PChainLink(pFirst1)); // Put some values in chain 2 for i := 0 to 99 do begin ptr := PItem(oChainPool.NewLink); with ptr^ do begin // put here the code that fills in new item's fields. FValue := i; FValue2 := 100 - i; end; ChainPools.LinkTo(PChainLink(ptr), PChainLink(pFirst2)); end; // Example for scanning chain. nValue := 30; ptr := PItem(ChainPools.ScanChain(PChainLink(pFirst2), oSomeObject.FindValue2, @nValue)); if ptr = nil then WriteLn('Item is not found') else WriteLn('FValue = ', ptr^.FValue); // Scan chain ptr := pFirst2; while ptr <> nil do begin if ptr^.FValue2 = 30 then begin WriteLn('FValue = ', ptr^.FValue); Break; end; ptr := ptr^.FNext; end; // Destroy pool oChainPool.Free; end. Это программное обеспечение на языке Delphi, демонстрирующее использование класса Программа создает два цепи: chain1 и chain2. Chain1 содержит значения от 0 до 99, а chain2 - значения от 0 до 99 с соответствующими "отраженными" значениями (т.е., значение отражается вокруг центра диапазона). Затем программа использует различные методы класса
Программа также демонстрирует, как использовать кастомную callback-функцию ( Обратите внимание, что это только примерный код, и вам может потребоваться его адаптация в соответствии с вашими конкретными требованиями. Кроме того, реализация класса Вот описание статьи на русском языке: «Быстрые списки» Автор: Vitaly Sergienko WEB-сайт: http://delphibase.endimus.com В статье описывается класс `TChainPool` для работы с связными списками (цепочками) в Delphi. Класс позволяет создавать, удалять и ман Комментарии и вопросыПолучайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |