spscringbuffer.pas 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. unit SPSCRingBuffer;
  2. {
  3. Single Producer Single Consumer (SPSC) Ring Buffer .
  4. Copyright (C) 2022 Dimitrios Chr. Ioannidis.
  5. Nephelae - https://www.nephelae.eu
  6. https://www.nephelae.eu/
  7. Licensed under the MIT License (MIT).
  8. See licence file in root directory.
  9. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  10. ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  11. TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  12. PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
  13. SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  14. ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  15. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  17. OTHER DEALINGS IN THE SOFTWARE.
  18. }
  19. {$mode ObjFPC}{$H+}
  20. interface
  21. uses
  22. Classes, SysUtils;
  23. type
  24. { TSPSCRingBuffer }
  25. TSPSCRingBuffer = class(TObject)
  26. private
  27. FMemoryData: Pointer;
  28. FMemorySize, FReadIndex, FWriteIndex: PtrUInt;
  29. function MaskIndex(const AValue: PtrUInt): PtrUInt;
  30. function GetEmpty: boolean;
  31. function GetFull: boolean;
  32. function GetCapacity: PtrUInt;
  33. protected
  34. function ReadByte: byte;
  35. procedure WriteByte(const AValue: byte);
  36. function PeekByte(AIndex: PtrUInt = 0): byte;
  37. public
  38. constructor Create(const ASize: PtrUInt);
  39. destructor Destroy; override;
  40. function Read(out ABuffer; const ALength: PtrUInt): PtrUInt;
  41. function Write(const ABuffer; const ALength: PtrUInt): PtrUInt;
  42. function Peek(out ABuffer; ALength: PtrUInt): PtrUInt;
  43. procedure AdvanceReadIdx(ACount: PtrUInt = 1);
  44. property Empty: boolean read GetEmpty;
  45. property Full: boolean read GetFull;
  46. property Size: PtrUInt read FMemorySize;
  47. end;
  48. implementation
  49. { TSPSCRingBuffer }
  50. constructor TSPSCRingBuffer.Create(const ASize: PtrUInt);
  51. begin
  52. inherited Create;
  53. FReadIndex := 0;
  54. FWriteIndex := 0;
  55. FMemorySize := ASize;
  56. Getmem(FMemoryData, FMemorySize);
  57. end;
  58. destructor TSPSCRingBuffer.Destroy;
  59. begin
  60. Freemem(FMemoryData, FMemorySize);
  61. inherited Destroy;
  62. end;
  63. function TSPSCRingBuffer.GetEmpty: boolean; //inline;
  64. begin
  65. Result := FReadIndex = FWriteIndex;
  66. end;
  67. function TSPSCRingBuffer.GetFull: boolean; //inline;
  68. begin
  69. Result := GetCapacity = FMemorySize - 1;
  70. end;
  71. function TSPSCRingBuffer.MaskIndex(const AValue: PtrUInt): PtrUInt; //inline;
  72. begin
  73. Result := AValue and (FMemorySize - 1);
  74. end;
  75. // See : https://forum.lazarus.freepascal.org/index.php/topic,59796.msg446453.html#msg446453
  76. function TSPSCRingBuffer.GetCapacity: PtrUInt; //inline;
  77. begin
  78. {$PUSH}
  79. {$Q-}
  80. {$R-}
  81. Result := MaskIndex(FWriteIndex - FReadIndex);
  82. {$POP}
  83. end;
  84. function TSPSCRingBuffer.ReadByte: byte; inline;
  85. begin
  86. Result := pbyte(FMemoryData)[MaskIndex(FReadIndex)];
  87. {$PUSH}
  88. {$Q-}
  89. Inc(FReadIndex);
  90. {$POP}
  91. end;
  92. procedure TSPSCRingBuffer.WriteByte(const AValue: byte); inline;
  93. begin
  94. pbyte(FMemoryData)[MaskIndex(FWriteIndex)] := AValue;
  95. {$PUSH}
  96. {$Q-}
  97. Inc(FWriteIndex);
  98. {$POP}
  99. end;
  100. function TSPSCRingBuffer.PeekByte(AIndex: PtrUInt): byte;
  101. begin
  102. {$PUSH}
  103. {$Q-}
  104. Result := pbyte(FMemoryData)[MaskIndex(FReadIndex + AIndex)];
  105. {$POP}
  106. end;
  107. function TSPSCRingBuffer.Read(out ABuffer; const ALength: PtrUInt): PtrUInt;
  108. begin
  109. Result := 0;
  110. while (not Empty) and (Result < ALength) do
  111. begin
  112. pbyte(@ABuffer + Result)^ := ReadByte;
  113. Inc(Result);
  114. end;
  115. end;
  116. function TSPSCRingBuffer.Write(const ABuffer; const ALength: PtrUInt): PtrUInt;
  117. begin
  118. Result := 0;
  119. while (not Full) and (Result < ALength) do
  120. begin
  121. WriteByte(pbyte(@ABuffer + Result)^);
  122. Inc(Result);
  123. end;
  124. end;
  125. function TSPSCRingBuffer.Peek(out ABuffer; ALength: PtrUInt): PtrUInt;
  126. begin
  127. Result := 0;
  128. while (Result < ALength) and (Result < GetCapacity) do
  129. begin
  130. pbyte(@ABuffer + Result)^ := PeekByte(Result);
  131. Inc(Result);
  132. end;
  133. end;
  134. procedure TSPSCRingBuffer.AdvanceReadIdx(ACount: PtrUInt);
  135. begin
  136. {$PUSH}
  137. {$Q-}
  138. Inc(FReadIndex, ACount);
  139. {$POP}
  140. end;
  141. end.