aboutsummaryrefslogtreecommitdiffstats
path: root/src/Classes/URingBuffer.pas
blob: ce51e209c58edc2011131e130b64e4605520431c (plain) (blame)
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.