From 137b11bf678df953430c92513c0dfae280fdf2f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:04:38 +0000 Subject: [PATCH 1/3] Initial plan From ab59e77d0f87dd3c7a40fde03521eb136bafc4cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:08:36 +0000 Subject: [PATCH 2/3] docs: add module documentation in docs/ and update README --- README.md | 130 ++++++++++++++++++++++- docs/README.md | 42 ++++++++ docs/api-reference.md | 235 ++++++++++++++++++++++++++++++++++++++++++ docs/dm_sw_ring.md | 67 ++++++++++++ docs/examples.md | 177 +++++++++++++++++++++++++++++++ 5 files changed, 649 insertions(+), 2 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/api-reference.md create mode 100644 docs/dm_sw_ring.md create mode 100644 docs/examples.md diff --git a/README.md b/README.md index ddf8e6f..aede4f3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,128 @@ -# dm_sw_ring -Helper module with software ring implementation +# DM_SW_RING - DMOD Software Ring Buffer Module + +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +A DMOD (Dynamic Modular System) module providing a general-purpose software ring buffer (circular buffer) implementation for embedded systems. + +## Features + +- **Byte-level Ring Buffer**: Stores arbitrary binary data byte-by-byte with configurable capacity +- **Overwrite Mode**: Optionally discard oldest data when the buffer is full (`dm_sw_ring_flags_drop_old_data`) +- **Thread Safety**: Optional mutex-based synchronization for use in multi-threaded environments (`dm_sw_ring_flags_mutex_sync`) +- **Blocking Writes**: Optionally block a writer until space becomes available (`dm_sw_ring_flags_wait_for_space`) +- **Blocking Reads**: Optionally block a reader until at least some data is available (`dm_sw_ring_flags_wait_for_some_data`) or until all requested data is available (`dm_sw_ring_flags_wait_for_all_data`) +- **Non-destructive Peek**: Inspect buffered data without consuming it +- **Discard and Clear**: Remove data from the buffer without reading it +- **DMOD Integration**: Follows the DMOD module lifecycle (`dmod_init` / `dmod_deinit`) + +## Quick Start + +### Installation + +Using `dmf-get` from the DMOD release package: + +```bash +dmf-get install dm_sw_ring +``` + +### Basic Usage + +```c +#include "dm_sw_ring.h" + +// Create a 64-byte ring buffer with default settings +dm_sw_ring_t ring = dm_sw_ring_create(64, dm_sw_ring_flags_default); + +// Write data +uint8_t tx[] = {1, 2, 3, 4}; +dm_sw_ring_capacity_t written = dm_sw_ring_write(ring, tx, sizeof(tx)); + +// Read data +uint8_t rx[4]; +dm_sw_ring_capacity_t read = dm_sw_ring_read(ring, rx, sizeof(rx)); + +// Destroy when done +dm_sw_ring_destroy(ring); +``` + +## Building + +### Prerequisites + +- CMake 3.18 or higher +- C99-compatible compiler +- DMOD framework (automatically fetched via CMake FetchContent) + +### Build Commands + +```bash +# Configure +cmake -B build + +# Build +cmake --build build +``` + +## Documentation + +Comprehensive documentation is available in the `docs/` directory: + +- **[dm_sw_ring.md](docs/dm_sw_ring.md)** - Module overview and architecture +- **[api-reference.md](docs/api-reference.md)** - Complete API documentation +- **[examples.md](docs/examples.md)** - Usage examples + +View documentation using `dmf-man`: + +```bash +dmf-man dm_sw_ring # Main documentation +dmf-man dm_sw_ring api # API reference +dmf-man dm_sw_ring examples # Usage examples +``` + +## Project Structure + +``` +dm_sw_ring/ +├── docs/ # Documentation (markdown format) +├── include/ # Public headers +│ └── dm_sw_ring.h # Main API (types, flags, function declarations) +├── src/ +│ └── dm_sw_ring.c # Core implementation +├── tests/ +│ ├── CMakeLists.txt # Test build configuration +│ └── test_dm_sw_ring.c # Unit tests +├── CMakeLists.txt # Build configuration +├── dm_sw_ring.dmr # DMOD resource file +└── manifest.dmm # DMOD manifest +``` + +## Contributing + +Contributions are welcome! Please: + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Authors + +- Patryk Kubiak - Initial work + +## Related Projects + +- [DMOD](https://github.com/choco-technologies/dmod) - Dynamic Modular System framework +- [DMGPIO](https://github.com/choco-technologies/dmgpio) - DMOD GPIO Driver Module +- [DMHAMAN](https://github.com/choco-technologies/dmhaman) - DMOD Handler Manager + +## Support + +For issues, questions, or contributions: + +- Open an issue on GitHub +- Check the documentation in `docs/` +- Use `dmf-man dm_sw_ring` for command-line help diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..dbdfa92 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,42 @@ +# DM_SW_RING Documentation + +Welcome to the DM_SW_RING module documentation. + +## Contents + +- **[dm_sw_ring.md](dm_sw_ring.md)** - Module overview and architecture +- **[api-reference.md](api-reference.md)** - Complete API documentation +- **[examples.md](examples.md)** - Usage examples + +## Quick Reference + +```c +#include "dm_sw_ring.h" + +// Create a 64-byte ring buffer with default settings (drop-old, mutex, blocking) +dm_sw_ring_t ring = dm_sw_ring_create(64, dm_sw_ring_flags_default); + +// Write bytes into the buffer +uint8_t data[] = {0xAA, 0xBB, 0xCC}; +dm_sw_ring_capacity_t written = dm_sw_ring_write(ring, data, sizeof(data)); + +// Read bytes from the buffer +uint8_t buf[3]; +dm_sw_ring_capacity_t read = dm_sw_ring_read(ring, buf, sizeof(buf)); + +// Peek without consuming +dm_sw_ring_peek(ring, buf, 1); + +// Discard bytes without reading +dm_sw_ring_discard(ring, 1); + +// Inspect state +dm_sw_ring_capacity_t cap = dm_sw_ring_capacity(ring); +dm_sw_ring_capacity_t used = dm_sw_ring_size(ring); +dm_sw_ring_capacity_t free_ = dm_sw_ring_available_space(ring); +bool full = dm_sw_ring_is_full(ring); +bool empty = dm_sw_ring_is_empty(ring); + +// Destroy when done +dm_sw_ring_destroy(ring); +``` diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..ea0d7df --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,235 @@ +# DM_SW_RING API Reference + +## Types + +### `dm_sw_ring_t` + +Opaque handle to a software ring buffer instance. All API functions operate on this handle. + +```c +typedef struct dm_sw_ring* dm_sw_ring_t; +``` + +--- + +### `dm_sw_ring_capacity_t` + +Unsigned 32-bit integer representing a byte count (capacity, size, or number of bytes to transfer). + +```c +typedef uint32_t dm_sw_ring_capacity_t; +``` + +--- + +### `dm_sw_ring_flags_t` + +Bitmask controlling ring buffer behavior. Values can be combined with `|`. + +```c +typedef enum +{ + dm_sw_ring_flags_drop_old_data = (1 << 0), + dm_sw_ring_flags_mutex_sync = (1 << 1), + dm_sw_ring_flags_wait_for_space = (1 << 2), + dm_sw_ring_flags_wait_for_data = (1 << 3), + dm_sw_ring_flags_wait_for_some_data = dm_sw_ring_flags_wait_for_data, + dm_sw_ring_flags_wait_for_all_data = dm_sw_ring_flags_wait_for_data | (1 << 4), + + dm_sw_ring_flags_default = dm_sw_ring_flags_drop_old_data + | dm_sw_ring_flags_mutex_sync + | dm_sw_ring_flags_wait_for_space + | dm_sw_ring_flags_wait_for_some_data, +} dm_sw_ring_flags_t; +``` + +| Value | Bit | Description | +|-------|-----|-------------| +| `dm_sw_ring_flags_drop_old_data` | 0 | Overwrite oldest data when buffer is full | +| `dm_sw_ring_flags_mutex_sync` | 1 | Use a DMOD mutex for thread-safe access | +| `dm_sw_ring_flags_wait_for_space` | 2 | Block writer until space is available | +| `dm_sw_ring_flags_wait_for_some_data` | 3 | Block reader until at least one byte is available | +| `dm_sw_ring_flags_wait_for_all_data` | 3+4 | Block reader until all requested bytes are available | +| `dm_sw_ring_flags_default` | — | `drop_old_data \| mutex_sync \| wait_for_space \| wait_for_some_data` | + +--- + +## Functions + +### `dm_sw_ring_create` + +Create a new software ring buffer instance. + +```c +dm_sw_ring_t dm_sw_ring_create(dm_sw_ring_capacity_t capacity, dm_sw_ring_flags_t flags); +``` + +**Parameters:** +- `capacity` – Buffer size in bytes. Must be greater than 0 and at most `INT32_MAX`. +- `flags` – Bitmask of `dm_sw_ring_flags_t` values that control synchronization and overflow behaviour. + +**Returns:** A valid `dm_sw_ring_t` handle on success, or `NULL` on failure (invalid capacity or memory allocation error). + +--- + +### `dm_sw_ring_destroy` + +Destroy a ring buffer instance and release all associated resources (buffer memory, mutex, semaphores). + +```c +void dm_sw_ring_destroy(dm_sw_ring_t ring); +``` + +**Parameters:** +- `ring` – Handle previously returned by `dm_sw_ring_create`. + +> **Note:** After `dm_sw_ring_destroy` returns, the handle is invalid and must not be used. + +--- + +### `dm_sw_ring_write` + +Write bytes into the ring buffer. + +```c +dm_sw_ring_capacity_t dm_sw_ring_write(dm_sw_ring_t ring, const void* data, dm_sw_ring_capacity_t length); +``` + +**Parameters:** +- `ring` – Ring buffer handle. +- `data` – Pointer to the source data. Must not be `NULL` when `length > 0`. +- `length` – Number of bytes to write. + +**Returns:** Number of bytes actually written. This may be less than `length` if the buffer is full and neither `drop_old_data` nor `wait_for_space` is set. Returns `0` on error. + +**Behaviour summary:** + +| Flags set | Buffer full → | +|-----------|---------------| +| `drop_old_data` | Oldest bytes are overwritten | +| `wait_for_space` | Writer blocks until space is available | +| Neither | Returns the number of bytes that fit; remaining bytes are silently dropped | + +--- + +### `dm_sw_ring_read` + +Read and consume bytes from the ring buffer. + +```c +dm_sw_ring_capacity_t dm_sw_ring_read(dm_sw_ring_t ring, void* buffer, dm_sw_ring_capacity_t length); +``` + +**Parameters:** +- `ring` – Ring buffer handle. +- `buffer` – Destination buffer. Must not be `NULL` when `length > 0`. +- `length` – Maximum number of bytes to read. + +**Returns:** Number of bytes actually read. With `wait_for_some_data` this is at least 1 (unless `ring` is invalid). With `wait_for_all_data` the call blocks until `length` bytes have been read. Without either wait flag the call returns immediately with however many bytes are available. + +--- + +### `dm_sw_ring_capacity` + +Return the total capacity of the ring buffer in bytes. + +```c +dm_sw_ring_capacity_t dm_sw_ring_capacity(dm_sw_ring_t ring); +``` + +**Returns:** Buffer capacity as passed to `dm_sw_ring_create`, or `0` on error. + +--- + +### `dm_sw_ring_size` + +Return the number of bytes currently stored in the ring buffer. + +```c +dm_sw_ring_capacity_t dm_sw_ring_size(dm_sw_ring_t ring); +``` + +**Returns:** Number of bytes available to read, or `0` on error. + +--- + +### `dm_sw_ring_available_space` + +Return the number of free bytes remaining in the ring buffer. + +```c +dm_sw_ring_capacity_t dm_sw_ring_available_space(dm_sw_ring_t ring); +``` + +**Returns:** `capacity - size`, or `0` on error. + +--- + +### `dm_sw_ring_peek` + +Copy bytes from the front of the ring buffer into `buffer` without consuming them. + +```c +int32_t dm_sw_ring_peek(dm_sw_ring_t ring, void* buffer, dm_sw_ring_capacity_t length); +``` + +**Parameters:** +- `ring` – Ring buffer handle. +- `buffer` – Destination buffer. Must not be `NULL` when `length > 0`. +- `length` – Maximum number of bytes to peek. + +**Returns:** Number of bytes copied (may be less than `length` if fewer bytes are stored), or `-1` on error (e.g. `NULL` buffer with non-zero length, or invalid handle). + +> **Note:** Peek does not advance the read pointer; subsequent reads will return the same bytes. + +--- + +### `dm_sw_ring_discard` + +Remove bytes from the front of the ring buffer without copying them to a destination buffer. + +```c +int32_t dm_sw_ring_discard(dm_sw_ring_t ring, dm_sw_ring_capacity_t length); +``` + +**Parameters:** +- `ring` – Ring buffer handle. +- `length` – Number of bytes to discard. Clamped to the current buffer size. + +**Returns:** Number of bytes actually discarded, `0` if `length` is 0, or `-1` on error (invalid handle). + +--- + +### `dm_sw_ring_clear` + +Remove all bytes from the ring buffer, resetting it to an empty state. + +```c +int32_t dm_sw_ring_clear(dm_sw_ring_t ring); +``` + +**Returns:** `0` on success, or `-1` on error (invalid handle). + +--- + +### `dm_sw_ring_is_full` + +Check whether the ring buffer is completely full. + +```c +bool dm_sw_ring_is_full(dm_sw_ring_t ring); +``` + +**Returns:** `true` if `size == capacity`, `false` otherwise (or on error). + +--- + +### `dm_sw_ring_is_empty` + +Check whether the ring buffer contains no data. + +```c +bool dm_sw_ring_is_empty(dm_sw_ring_t ring); +``` + +**Returns:** `true` if `size == 0`, `false` otherwise (or on error). diff --git a/docs/dm_sw_ring.md b/docs/dm_sw_ring.md new file mode 100644 index 0000000..9a70e8a --- /dev/null +++ b/docs/dm_sw_ring.md @@ -0,0 +1,67 @@ +# DM_SW_RING Module Overview + +## Introduction + +DM_SW_RING is a DMOD (Dynamic Modular System) module that provides a lightweight, general-purpose software ring buffer (also known as a circular buffer or FIFO) for embedded systems. It stores arbitrary binary data at byte granularity and supports optional thread-safety through mutex synchronization and semaphore-based blocking for producers and consumers. + +## Architecture + +``` +┌─────────────────────────────────────────┐ +│ Application Code │ +└────────────────┬────────────────────────┘ + │ dm_sw_ring API +┌────────────────▼────────────────────────┐ +│ dm_sw_ring │ +│ (Software ring buffer implementation) │ +│ │ +│ - Byte-level circular buffer │ +│ - Optional mutex synchronization │ +│ - Optional blocking on read/write │ +│ - Peek / discard / clear operations │ +└─────────────────────────────────────────┘ +``` + +## Key Concepts + +### Ring Buffer Mechanics + +The buffer maintains a contiguous block of memory of a fixed `capacity` (in bytes). Internally it tracks a `head` (read pointer), a `tail` (write pointer), and a `count` (number of bytes currently stored). When the tail wraps around past the end of the buffer it restarts from index 0, forming the "ring". + +### Behavior Flags + +The behavior of the ring buffer is controlled by a bitmask of `dm_sw_ring_flags_t` values supplied at creation time: + +| Flag | Description | +|------|-------------| +| `dm_sw_ring_flags_drop_old_data` | When the buffer is full, the oldest bytes are silently overwritten to make room for new data | +| `dm_sw_ring_flags_mutex_sync` | A DMOD mutex is created to serialize concurrent access; without this flag all operations use a critical section | +| `dm_sw_ring_flags_wait_for_space` | A writer blocks (on a semaphore) until enough space becomes available instead of dropping data immediately | +| `dm_sw_ring_flags_wait_for_some_data` | A reader blocks until at least one byte is available | +| `dm_sw_ring_flags_wait_for_all_data` | A reader blocks until **all** requested bytes are available | +| `dm_sw_ring_flags_default` | Combination of `drop_old_data`, `mutex_sync`, `wait_for_space`, and `wait_for_some_data` | + +> **Note:** `wait_for_space` and `drop_old_data` are complementary: if `wait_for_space` is set, a writer that finds the buffer full will block rather than discard. If neither flag is set, a write to a full buffer returns 0 bytes written. + +### Thread Safety + +When `dm_sw_ring_flags_mutex_sync` is set, a mutex is created at buffer construction time. Every public API function acquires this mutex before operating on the internal state and releases it afterwards. Blocking semaphore operations (`wait_for_space` / `wait_for_data`) are performed *outside* the mutex to avoid deadlocks. + +Without `dm_sw_ring_flags_mutex_sync`, a DMOD critical section (interrupt-safe spinlock) is used instead, which is sufficient for single-threaded or interrupt-driven environments. + +## Module Files + +``` +dm_sw_ring/ +├── docs/ # Documentation (markdown format) +├── include/ +│ └── dm_sw_ring.h # Public API: types, flags, function declarations +├── src/ +│ └── dm_sw_ring.c # Implementation +├── tests/ +│ ├── CMakeLists.txt +│ └── test_dm_sw_ring.c +├── CMakeLists.txt +├── dm_sw_ring.dmr # DMOD resource file +└── manifest.dmm # DMOD manifest +``` diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..7b94b93 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,177 @@ +# DM_SW_RING Usage Examples + +## Example 1: Simple FIFO Queue + +Create a 16-byte ring buffer, write some data, and read it back in FIFO order. + +```c +#include "dm_sw_ring.h" +#include +#include + +void simple_fifo_example(void) +{ + // Create a 16-byte buffer with mutex sync but no blocking/overwrite + dm_sw_ring_t ring = dm_sw_ring_create(16, dm_sw_ring_flags_mutex_sync); + + uint8_t tx[] = {1, 2, 3, 4, 5}; + dm_sw_ring_write(ring, tx, sizeof(tx)); + + uint8_t rx[5]; + dm_sw_ring_capacity_t n = dm_sw_ring_read(ring, rx, sizeof(rx)); + // n == 5, rx == {1, 2, 3, 4, 5} + + dm_sw_ring_destroy(ring); +} +``` + +--- + +## Example 2: Overflow — Drop Oldest Data + +When `dm_sw_ring_flags_drop_old_data` is set, writing beyond capacity silently discards the oldest bytes, preserving the most recent data. + +```c +#include "dm_sw_ring.h" + +void drop_old_example(void) +{ + // 4-byte buffer, drop oldest bytes on overflow + dm_sw_ring_t ring = dm_sw_ring_create(4, dm_sw_ring_flags_drop_old_data + | dm_sw_ring_flags_mutex_sync); + + uint8_t tx[] = {10, 11, 12, 13, 14}; // 5 bytes → overflows by 1 + dm_sw_ring_write(ring, tx, sizeof(tx)); + // Buffer now contains: {11, 12, 13, 14} (byte 10 was dropped) + + uint8_t rx[4]; + dm_sw_ring_read(ring, rx, sizeof(rx)); + // rx == {11, 12, 13, 14} + + dm_sw_ring_destroy(ring); +} +``` + +--- + +## Example 3: Blocking Producer / Consumer (RTOS) + +In an RTOS environment, use the default flags to have writers block when the buffer is full and readers block when the buffer is empty. + +```c +#include "dm_sw_ring.h" + +static dm_sw_ring_t g_ring; + +// Producer task — runs in its own RTOS thread +void producer_task(void) +{ + uint8_t byte = 0; + while (1) + { + // Blocks until space is available (dm_sw_ring_flags_wait_for_space) + dm_sw_ring_write(g_ring, &byte, 1); + byte++; + } +} + +// Consumer task — runs in its own RTOS thread +void consumer_task(void) +{ + uint8_t byte; + while (1) + { + // Blocks until at least one byte is available (dm_sw_ring_flags_wait_for_some_data) + dm_sw_ring_read(g_ring, &byte, 1); + // process byte... + } +} + +void rtos_example_init(void) +{ + // Default flags: drop_old_data | mutex_sync | wait_for_space | wait_for_some_data + g_ring = dm_sw_ring_create(64, dm_sw_ring_flags_default); +} +``` + +--- + +## Example 4: Non-Destructive Peek + +Use `dm_sw_ring_peek` to inspect data without consuming it — useful for framing protocols where you need to check a header before deciding how many bytes to read. + +```c +#include "dm_sw_ring.h" + +void peek_example(dm_sw_ring_t ring) +{ + uint8_t header[2]; + + // Peek at the first 2 bytes without consuming them + int32_t peeked = dm_sw_ring_peek(ring, header, sizeof(header)); + if (peeked < 2) + { + return; // Not enough data yet + } + + uint16_t payload_length = (uint16_t)((header[0] << 8) | header[1]); + + if (dm_sw_ring_size(ring) < 2u + payload_length) + { + return; // Full frame not yet available — come back later + } + + // Discard the header and read the payload + dm_sw_ring_discard(ring, 2); + + uint8_t payload[256]; + dm_sw_ring_read(ring, payload, payload_length); + // process payload... +} +``` + +--- + +## Example 5: Discard and Clear + +Remove data without reading it. + +```c +#include "dm_sw_ring.h" + +void discard_clear_example(void) +{ + dm_sw_ring_t ring = dm_sw_ring_create(8, dm_sw_ring_flags_mutex_sync); + + uint8_t data[] = {1, 2, 3, 4, 5, 6}; + dm_sw_ring_write(ring, data, sizeof(data)); + + // Drop the first 3 bytes + dm_sw_ring_discard(ring, 3); + // Buffer now contains: {4, 5, 6} + + // Wipe everything + dm_sw_ring_clear(ring); + // Buffer is empty + + dm_sw_ring_destroy(ring); +} +``` + +--- + +## Example 6: State Inspection + +```c +#include "dm_sw_ring.h" +#include + +void inspect_example(dm_sw_ring_t ring) +{ + printf("capacity : %u\n", dm_sw_ring_capacity(ring)); + printf("bytes used : %u\n", dm_sw_ring_size(ring)); + printf("bytes free : %u\n", dm_sw_ring_available_space(ring)); + printf("is_full : %s\n", dm_sw_ring_is_full(ring) ? "yes" : "no"); + printf("is_empty : %s\n", dm_sw_ring_is_empty(ring) ? "yes" : "no"); +} +``` From ba23342f834208cb2e6a81a175cbbee641f555c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:09:14 +0000 Subject: [PATCH 3/3] docs: fix American English spelling in api-reference.md --- docs/api-reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index ea0d7df..5991efa 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -66,7 +66,7 @@ dm_sw_ring_t dm_sw_ring_create(dm_sw_ring_capacity_t capacity, dm_sw_ring_flags_ **Parameters:** - `capacity` – Buffer size in bytes. Must be greater than 0 and at most `INT32_MAX`. -- `flags` – Bitmask of `dm_sw_ring_flags_t` values that control synchronization and overflow behaviour. +- `flags` – Bitmask of `dm_sw_ring_flags_t` values that control synchronization and overflow behavior. **Returns:** A valid `dm_sw_ring_t` handle on success, or `NULL` on failure (invalid capacity or memory allocation error). @@ -102,7 +102,7 @@ dm_sw_ring_capacity_t dm_sw_ring_write(dm_sw_ring_t ring, const void* data, dm_s **Returns:** Number of bytes actually written. This may be less than `length` if the buffer is full and neither `drop_old_data` nor `wait_for_space` is set. Returns `0` on error. -**Behaviour summary:** +**Behavior summary:** | Flags set | Buffer full → | |-----------|---------------|