Page 1 of 1

A ringbuffer in PB

Posted: Sat Sep 08, 2012 4:32 pm
by infratec
Hi,

I needed a ringbuffer to solve my ReadNetworkData() problem.

So here it is (save it as RingBuffer.pbi):

Code: Select all

;
; RingBuffer implementation
;
; by infratec

Enumeration
  #RingBuffer_Getmode
  #RingBuffer_Peekmode
EndEnumeration


Structure RingBuffer_Structure
  *RingBuffer
  Size.i
  ReadPtr.i
  WritePtr.i
EndStructure


Procedure.i RingBuffer_Init(*RB.RingBuffer_Structure, Size.i)
  
  *RB\RingBuffer = AllocateMemory(Size)
  If *RB\RingBuffer
    *RB\Size = Size
    *RB\ReadPtr = 0
    *RB\WritePtr = 0
  Else
    *RB\Size = 0
  EndIf
  
  ProcedureReturn *RB\Size
  
EndProcedure


Procedure.i RingBuffer_BytesToRead(*RB.RingBuffer_Structure)
  
  Protected Result.i
  
  If *RB\WritePtr = *RB\ReadPtr
    Result = 0
  ElseIf *RB\WritePtr > *RB\ReadPtr
    Result = *RB\WritePtr - *RB\ReadPtr
  Else
    Result = *RB\Size - (*RB\ReadPtr - *RB\WritePtr)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure


Procedure.i RingBuffer_Put(*RB.RingBuffer_Structure, *Src, Bytes.i)
  
  Protected Result.i, Help.i
  
  Result = #False
  
  If *RB\Size - RingBuffer_BytesToRead(*RB) >= Bytes
    If *RB\WritePtr + Bytes <= *RB\Size
      CopyMemory(*Src, *RB\RingBuffer + *RB\WritePtr, Bytes)
      *RB\WritePtr + Bytes
    Else
      Help = *RB\Size - *RB\WritePtr
      CopyMemory(*Src, *RB\RingBuffer + *RB\WritePtr, Help)
      CopyMemory(*Src + Help, *RB\RingBuffer, Bytes - Help)
      *RB\WritePtr = Bytes - Help
    EndIf
    
    Result = #True
    
  EndIf
  
  ProcedureReturn Result
  
EndProcedure


Procedure.i RingBuffer_Get(*RB.RingBuffer_Structure, *Dst, Bytes.i, Mode.i = #RingBuffer_Getmode)
  
  Protected AvailableBytes.i, Part.i
  
  AvailableBytes = RingBuffer_BytesToRead(*RB)
  If AvailableBytes > 0
    If AvailableBytes < Bytes : Bytes = AvailableBytes : EndIf
    If *RB\Size - *RB\ReadPtr >= Bytes
      CopyMemory(*RB\RingBuffer + *RB\ReadPtr, *Dst, Bytes)
      If Mode = #RingBuffer_Getmode : *RB\ReadPtr + Bytes : EndIf
    Else
      Part = *RB\Size - *RB\ReadPtr
      CopyMemory(*RB\RingBuffer + *RB\ReadPtr, *Dst, Part)
      CopyMemory(*RB\RingBuffer, *Dst + Part, Bytes - Part)
      If Mode = #RingBuffer_Getmode : *RB\ReadPtr = Bytes - Part : EndIf
    EndIf
  Else
    Bytes = 0
  EndIf
  
  ProcedureReturn Bytes
  
EndProcedure


Procedure RingBuffer_Clear(*RB.RingBuffer_Structure)
  *RB\ReadPtr = 0
  *RB\WritePtr = 0
EndProcedure


Procedure RingBuffer_Free(*RB.RingBuffer_Structure)
  FreeMemory(*RB\RingBuffer)
  *RB\Size = 0
  *RB\ReadPtr = 0
  *RB\WritePtr = 0
EndProcedure
To use it:
1. Define RB.RingBuffer_Structure
2. RingBuffer_Init(@RB, Your_wanted_size)

Use RingBuffer_Put() and RingBuffer_Get()
To look if something is available to read: RingBuffer_BytesToRead()

n. RingBuffer_Free(@RB)

With the optional parameter Mode = #RingBuffer_Peekmode of RingBuffer_Get(),
you can get data without removing them from the buffer.
(To test a received byte, before you read the whole packet, for example)

Have fun :mrgreen:

Bernd