1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
unit URingBuffer;
interface
{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
{$I switches.inc}
uses
SysUtils;
type
TRingBuffer = class
private
RingBuffer: PChar;
BufferCount: integer;
BufferSize: integer;
WritePos: integer;
ReadPos: integer;
public
constructor Create(Size: integer);
destructor Destroy; override;
function Read(Buffer: PChar; Count: integer): integer;
function Write(Buffer: PChar; Count: integer): integer;
procedure Flush();
end;
implementation
uses
Math;
constructor TRingBuffer.Create(Size: integer);
begin
BufferSize := Size;
GetMem(RingBuffer, Size);
if (RingBuffer = nil) then
raise Exception.Create('No memory');
end;
destructor TRingBuffer.Destroy;
begin
FreeMem(RingBuffer);
end;
function TRingBuffer.Read(Buffer: PChar; Count: integer): integer;
var
PartCount: integer;
begin
// adjust output count
if (Count > BufferCount) then
begin
//DebugWriteln('Read too much: ' + inttostr(count) +',count:'+ inttostr(BufferCount) + '/size:' + inttostr(BufferSize));
Count := BufferCount;
end;
// check if there is something to do
if (Count <= 0) then
begin
Result := Count;
Exit;
end;
// copy data to output buffer
// first step: copy from the area between the read-position and the end of the buffer
PartCount := Min(Count, BufferSize - ReadPos);
Move(RingBuffer[ReadPos], Buffer[0], PartCount);
// second step: if we need more data, copy from the beginning of the buffer
if (PartCount < Count) then
Move(RingBuffer[0], Buffer[0], Count-PartCount);
// mark the copied part of the buffer as free
BufferCount := BufferCount - Count;
ReadPos := (ReadPos + Count) mod BufferSize;
Result := Count;
end;
function TRingBuffer.Write(Buffer: PChar; Count: integer): integer;
var
PartCount: integer;
begin
// check for a reasonable request
if (Count <= 0) then
begin
Result := Count;
Exit;
end;
// skip input data if the input buffer is bigger than the ring-buffer
if (Count > BufferSize) then
begin
//DebugWriteln('Write skip data:' + inttostr(count) +',count:'+ inttostr(BufferCount) + '/size:' + inttostr(BufferSize));
Buffer := @Buffer[Count - BufferSize];
Count := BufferSize;
end;
// first step: copy to the area between the write-position and the end of the buffer
PartCount := Min(Count, BufferSize - WritePos);
Move(Buffer[0], RingBuffer[WritePos], PartCount);
// second step: copy data to front of buffer
if (PartCount < Count) then
Move(Buffer[PartCount], RingBuffer[0], Count-PartCount);
// update info
BufferCount := Min(BufferCount + Count, BufferSize);
WritePos := (WritePos + Count) mod BufferSize;
// if the buffer is full, we have to reposition the read-position
if (BufferCount = BufferSize) then
ReadPos := WritePos;
Result := Count;
end;
procedure TRingBuffer.Flush();
begin
ReadPos := 0;
WritePos := 0;
BufferCount := 0;
end;
end.
|