unit RingList;

interface

uses
  Windows, Classes, SysUtils;

type
  TRingList = class
  private
    FList : array of Pointer;
    FIndex : integer;
    FCapacity : integer;
    function GetItems(Index: integer): pointer;
    procedure SetItems(Index: integer; const Value: pointer);
    function GetCurrentIndex: integer;
    function GetNextIndex: integer;
  public
    constructor Create(ACapacity:integer); reintroduce;

    function CurrentItem:pointer;
    function NextItem:pointer;

    procedure IncIndex;

    property Items[Index:integer] : pointer read GetItems write SetItems;
    property CurrentIndex : integer read GetCurrentIndex;
    property NextIndex : integer read GetNextIndex;
    property Capacity : integer read FCapacity;
  end;

implementation

{ TRingList }

constructor TRingList.Create(ACapacity: integer);
begin
  inherited Create;

  FIndex := 0;
  FCapacity := ACapacity;

  SetLength(FList, ACapacity);
  FillChar(FList[0], SizeOf(FList), 0);
end;

function TRingList.CurrentItem: pointer;
begin
  Result := FList[FIndex mod Capacity];
end;

function TRingList.GetCurrentIndex: integer;
begin
  Result := FIndex mod Capacity;
end;

function TRingList.GetItems(Index: integer): pointer;
begin
  Result := FList[Index];
end;

function TRingList.GetNextIndex: integer;
begin
  Result := (FIndex + 1) mod FCapacity;
end;

procedure TRingList.IncIndex;
var
  iIndex : integer;
begin
  iIndex := InterlockedIncrement(FIndex);
  if iIndex >= FCapacity then
    InterlockedCompareExchange(FIndex, iIndex mod FCapacity, iIndex);
end;

function TRingList.NextItem: pointer;
var
  iIndex : integer;
begin
  // Memory Pool를 구현할 때, 여러 스레드에서 각자가 필요한 메모리 주소를
  // Lock-Free로 가져가도록 배려 했다.  즉, 같은 위치가 중복 배당 안되도록..
  iIndex := InterlockedIncrement(FIndex);
  if iIndex >= FCapacity then
    InterlockedCompareExchange(FIndex, iIndex mod FCapacity, iIndex);

  Result := FList[iIndex mod Capacity];
end;

procedure TRingList.SetItems(Index: integer; const Value: pointer);
begin
  FList[Index] := Value;
end;

end.

