unit UdpQueue;
interface
uses
Classes, SyncObjs, SysUtils;
type
{ Safe UDP packet size - 512 bytes. }
TUdpPacketRec = record
LocalPort: Integer;
PeerPort: Integer;
Priority: Byte;
LocalIP: AnsiString;
PeerIP: AnsiString;
Channel: AnsiString;
Data: AnsiString;
end;
TUdpPacketEvent = procedure(Sender: TObject; const UdpPacketRec: TUdpPacketRec) of object;
TUdpPacketQueueItem = class(TObject)
public
UdpPacketRec: TUdpPacketRec;
end;
// thread-safe packets queue with reusable items
TUdpPacketQueue = class(TObject)
private
FLock: TCriticalSection;
FList: TList;
FCapacity: Integer;
FCount: Integer;
function GetCapacity: Integer;
function GetCount: Integer;
procedure SetCapacity(const Value: Integer);
public
constructor Create(ACapacity: Integer);
destructor Destroy(); override;
{Add packet to queue, increase queue length. }
function Push(const APacket: TUdpPacketRec): Boolean;
{ Get packet from queue, decrease queue length. Returns False if queue empty }
function Pull(out APacket: TUdpPacketRec): Boolean;
{ Read packet from queue without decreasing queue length. Returns False if queue empty }
function Peek(out APacket: TUdpPacketRec): Boolean;
{ Remove all queue items and set queue length to 0 }
procedure Clear();
{ Minimal length of internal list, count of replaceable items
Queue can grow over capacity, but internal list can't be less, than capacity }
property Capacity: Integer read GetCapacity write SetCapacity;
{ Length of queue }
property Count: Integer read GetCount;
end;
implementation
{ TUdpPacketQueue }
constructor TUdpPacketQueue.Create(ACapacity: Integer);
begin
inherited Create();
FLock := TCriticalSection.Create();
FList := TList.Create();
FCapacity := 0;
FCount := 0;
SetCapacity(ACapacity);
end;
destructor TUdpPacketQueue.Destroy;
begin
SetCapacity(0);
FreeAndNil(FList);
FreeAndNil(FLock);
inherited;
end;
procedure TUdpPacketQueue.Clear();
var
Item: TUdpPacketQueueItem;
begin
if Assigned(FLock) then FLock.Acquire();
try
while FList.Count > 0 do
begin
Item := TUdpPacketQueueItem(FList.Items[0]);
Item.Free();
FList.Delete(0);
end;
FList.Clear();
FCount := 0;
finally
if Assigned(FLock) then FLock.Release();
end;
end;
function TUdpPacketQueue.Pull(out APacket: TUdpPacketRec): Boolean;
var
Item: TUdpPacketQueueItem;
i: Integer;
begin
Result := False;
if (Count <= 0) then Exit;
FLock.Acquire();
try
if (Count > 0) then
begin
Item := TUdpPacketQueueItem(FList.Items[0]);
APacket := Item.UdpPacketRec;
// shift all items to position of zero item
for i := 0 to FCount-2 do
begin
FList.Exchange(i, i+1);
end;
if FList.Count > FCapacity then
begin
// list size more than capacity, remove last item
Item := TUdpPacketQueueItem(FList.Items[FList.Count-1]);
Item.Free();
FList.Delete(FList.Count-1);
end;
Dec(FCount);
Result := True;
end;
finally
FLock.Release();
end;
end;
function TUdpPacketQueue.Peek(out APacket: TUdpPacketRec): Boolean;
var
Item: TUdpPacketQueueItem;
begin
Result := False;
if (Count <= 0) then Exit;
FLock.Acquire();
try
if (Count > 0) then
begin
Item := TUdpPacketQueueItem(FList.Items[0]);
APacket := Item.UdpPacketRec;
Result := True;
end;
finally
FLock.Release();
end;
end;
function TUdpPacketQueue.Push(const APacket: TUdpPacketRec): Boolean;
var
Item: TUdpPacketQueueItem;
begin
if Assigned(FLock) then
begin
FLock.Acquire();
try
if FCount < FList.Count then
begin
// put data to item at queue length position
Item := TUdpPacketQueueItem(FList.Items[FCount]);
end
else
begin
// queue length less than list size, add new list item
Item := TUdpPacketQueueItem.Create();
FList.Add(Item);
end;
Item.UdpPacketRec := APacket;
Inc(FCount);
finally
FLock.Release();
end;
Result := True;
end
else
Result := False;
end;
function TUdpPacketQueue.GetCapacity: Integer;
begin
Result := FCapacity;
end;
procedure TUdpPacketQueue.SetCapacity(const Value: Integer);
var
Item: TUdpPacketQueueItem;
begin
FLock.Acquire();
try
FCapacity := Value;
// add replaceable list items
while FList.Count < FCapacity do
begin
Item := TUdpPacketQueueItem.Create();
FList.Add(Item);
end;
while FList.Count > FCapacity do
begin
// list size is more, than desired capacity, remove last list item
Item := TUdpPacketQueueItem(FList.Items[FList.Count-1]);
Item.Free();
FList.Delete(FList.Count-1);
end;
finally
FLock.Release();
end;
end;
function TUdpPacketQueue.GetCount: Integer;
begin
Result := FCount;
end;
end.