Double Buffer
LibXR::DoubleBuffer is a double-buffer state manager designed for embedded scenarios. It is mainly used to control switching and filling across two half-buffers in high-speed paths such as DMA and USB. It manages the active/pending halves and their state bits, but does not allocate or free the backing storage itself.
Key Features
- Splits one contiguous memory block into two equal halves under the current implementation contract.
- Supports switching between the active buffer and the pending buffer.
- Provides direct access and data fill interfaces for both buffers.
- Supports default construction followed by later
Init()binding. - Exposes pending state plus auxiliary active/pending length metadata.
Interface Overview
Construction and initialization
DoubleBuffer() = default;
explicit DoubleBuffer(const LibXR::RawData& raw_data);
void Init(const LibXR::RawData& raw_data);
void Reset();
DoubleBuffer(raw_data)andInit(raw_data)use the same initialization path.raw_datamust satisfy the current half-split contract; an empty double buffer may be initialized withnullptr + 0.Reset()only clears runtime state and keeps the two bound halves attached.
Data Operation Interfaces
uint8_t* ActiveBuffer(): Get the currently active buffer.uint8_t* PendingBuffer(): Get the pending buffer.uint8_t* Buffer(int block): Access half0or1by stable block number.bool FillActive(const uint8_t* data, size_t len): Write data to the active buffer.bool FillPending(const uint8_t* data, size_t len): Write data to the pending buffer.void EnablePending(): Manually mark the current pending state as valid; this API does not copy data and does not switch the active block.bool HasPending() const: Check if there is a pending buffer ready to be switched.void Switch(): If pending is valid, flips the active block and clears the pending-valid bit.size_t GetPendingLength() const: Get the valid data length in the pending buffer.size_t GetActiveLength() const: Get the auxiliary length field of the active buffer.void SetPendingLength(size_t length): Set the auxiliary length field of the pending buffer.void SetActiveLength(size_t length): Set the auxiliary length field of the active buffer.int ActiveBlock() const/void SetActiveBlock(bool)/void FlipActiveBlock(): Directly control the active block index.size_t Size() const: Get the capacity of each buffer.
Additional notes:
- In the current implementation,
FillPending()both copies bytes into the pending half and updatespending_len_. FillActive()only copies bytes into the active half and does not updateactive_len_; if upper layers also use the length metadata, they must callSetActiveLength()explicitly.EnablePending()only changespending_valid_and does not setpending_len_automatically.
Usage Example
alignas(size_t) uint8_t mem[512] = {};
LibXR::RawData raw(mem, sizeof(mem));
LibXR::DoubleBuffer buf;
buf.Init(raw);
// Write to the current pending half and mark it ready for the next switch
buf.FillPending(data1, len1);
if (buf.HasPending()) {
buf.Switch(); // Switch to the new active buffer
}
If the upper layer writes bytes into the pending half by itself and only needs the state transition, it should usually also provide the length explicitly:
std::memcpy(buf.PendingBuffer(), data2, len2);
buf.SetPendingLength(len2);
buf.EnablePending();
Notes
- You must call
Switch()after filling the pending buffer to activate it. - The class itself does not allocate backing storage. The caller prepares it; this can be a static array or caller-managed dynamic memory, as long as the current alignment and size contract is satisfied.
FillPendingis not reentrant; ensure the pending state is false before calling.EnablePending()only changes the pending-valid bit. It does not copy data and does not synchronize the length fields automatically.
Application Scenarios
- USB CDC / UART data transmission
- Optimized DMA data streaming
- Ping-pong buffering communication mechanism