Segmented Buffers¶
It is a constantly recurring task to fill memory areas with certain data. The following is often added as an additional requirement:
Some parts always have the same content.
Special memory sections need to be attached or removed quickly and easily.
Certain parts, often the front part (Head) and the rear part (Tail), belonging together.
Several parts of the memory content should be easily reusable.
With the functionality available so far, complex operations such as the requesting of larger memory areas with subsequent copying of the previous contents and the addition of the new contents are necessary.
One Buffer
consists of a series of Segment
’s.
These segments reference the subordinate memory via an additional management structure (the Block
).
This means that different sections of the same Data
memory area can be organized by different buffers in a different order.
The individual elements are managed via reference counters. This means that the individual memory areas are always
released at a point in time when there is no further reference pointing to them.
FUNCTION_BLOCK FINAL Buffer IMPLEMENTS IBuffer
VAR_INPUT CONSTANT
udiSize : UDINT;
pData : __XWORD;
END_VAR
With this structure, the various contents from different memory areas can be chained, trimmed or reordered very efficiently. This is achieved by the requirement to copy as little memory as possible but to manipulate only the corresponding references to the memory as far as possible in a smart way.
/// The current buffer instance is chained with ``itfBuffer`` and thus extended accordingly.
METHOD ChainBuffer : ERROR
VAR_INPUT
itfBuffer : IBuffer;
END_VAR
/// The current buffer instance is sliced at the corresponding position
/// (specified by ``udiOffset`` and ``udiLength``)
/// and a new corresponding buffer instance is created and returned.
METHOD SliceBuffer : IBuffer
VAR_INPUT
udiOffset : UDINT;
udiLength : UDINT;
END_VAR
VAR_OUTPUT
eErrorID : ERROR;
END_VAR
Another way to create a new buffer instance is to use a buffer pool.
For each required buffer size a corresponding pool can be created.
The memory backing the pool can be defined statically (pPool > 0
and uidPoolSize > 0
) or dynamically.
FUNCTION_BLOCK FINAL BufferPool IMPLEMENTS IBufferPool
VAR_INPUT CONSTANT
udiBufferSize : UDINT;
udiInitialCapacity : UDINT := 5;
pPool : POINTER TO BYTE;
udiPoolSize : UDINT;
usiExtensionFactor : USINT;
END_VAR
METHOD PoolGetBuffer : __XWORD
VAR_INPUT
pData : __XWORD;
udiSize : UDINT;
END_VAR
VAR_OUTPUT
itfBuffer : IBuffer;
eErrorID : ERROR;
END_VAR
METHOD PoolExtend : ERROR
VAR_INPUT
udiBufferCount : UDINT;
END_VAR
METHOD PoolGetCapacity : UDINT
VAR_OUTPUT
eErrorID : ERROR;
END_VAR
The work with a buffer can be separated into several phases:
Collecting data from different sources as Segments (
Buffer
,PoolGetBuffer
).Trimming, chaining or reordering the data segments as necessary (
ChainBuffer
,SliceBuffer
).Traversing the content of a buffer (
MoveToBufferOffset
,GetSegmentDataPointer
,MoveToNextSegment
)Serializing of the discontinuous stored data content of a buffer, represented by the list of segments, to a final continuous data stream (
CopyBufferData
).
Beispiele¶
Statische Buffer Instanzen Buffer Instanzen aus einem Pool
Füllen eines Buffer aus SysSocket Verschicken eines Buffer über SysSocket