Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Bdd/features/steps/syslog_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ def run_example(context, extra_args=None, expected_messages=1):
(S24.05).

The pre-S13.18 implementation wrote `send N\\nquit\\n` upfront via
process.communicate() and relied on NullBuffer's synchronous Send to
process.communicate() and relied on PassthroughBuffer's synchronous Send to
guarantee delivery before exit. That assumption broke once the
target became buffered — `quit` could land before the service
thread had drained, losing the UDP packet. The prompt protocol
Expand Down
2 changes: 1 addition & 1 deletion Bdd/features/tcp_singletask.feature
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@tcp @windows_wip
Feature: TCP message delivery (single-task example)
The Linux single-task example (NullBuffer, synchronous send) sends
The Linux single-task example (PassthroughBuffer, synchronous send) sends
messages via TCP transport with RFC 6587 octet-counting framing.
Linux-only post-S13.18 — Windows TCP coverage now comes from
tcp_transport.feature via the BDD target. The single-task
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*`
| `SolidSyslogSwitchingSender.h` | System setup code composing multiple inner senders | `SolidSyslogSwitchingSenderConfig` (senders, senderCount, selector), `SolidSyslogSwitchingSenderSelector`, `SolidSyslogSwitchingSender_Create`, `_Destroy` |
| `SolidSyslogBuffer.h` | Library internals consuming a buffer (Service algorithm) | `SolidSyslogBuffer_Write`, `_Read` |
| `SolidSyslogBufferDefinition.h` | Buffer implementors (extension point) | `SolidSyslogBuffer` vtable struct |
| `SolidSyslogNullBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogNullBuffer_Create`, `_Destroy` |
| `SolidSyslogPassthroughBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogPassthroughBuffer_Create`, `_Destroy` |
| `SolidSyslogPosixMessageQueueBuffer.h` | System setup code using POSIX message queue buffer | `SolidSyslogPosixMessageQueueBuffer_Create`, `_Destroy` |
| `SolidSyslogCircularBuffer.h` | System setup code using an in-memory ring buffer (bare-metal / RTOS / Windows) | `SOLIDSYSLOG_CIRCULAR_BUFFER_HEADER_BYTES`, `SOLIDSYSLOG_CIRCULAR_BUFFER_RING_BYTES(maxMessages)` (friendly: N max-sized messages → ring bytes), `SolidSyslogCircularBuffer_Create(mutex, ring, ringBytes)`, `_Destroy(buffer)` (uint16-length-prefixed records, drop-newest on overflow, no-split wrap, mutex-injected synchronization). Instance struct lives in a library-internal static pool (E11); caller supplies the ring memory only. |
| `SolidSyslogMutex.h` | Any code holding a mutex handle | `SolidSyslogMutex_Lock`, `_Unlock` |
Expand All @@ -353,7 +353,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*`
| `SolidSyslogBlockDevice.h` | Library internals consuming a block device (BlockSequence inside BlockStore) and integrator code addressing blocks directly | `SolidSyslogBlockDevice_Acquire`, `_Dispose`, `_Exists`, `_Read`, `_Append`, `_WriteAt`, `_Size` (block-indexed I/O; Acquire makes a block ready for fresh writes, Dispose releases it) |
| `SolidSyslogBlockDeviceDefinition.h` | BlockDevice implementors (extension point) | `SolidSyslogBlockDevice` vtable struct (`Acquire`, `Dispose`, `Exists`, `Read`, `Append`, `WriteAt`, `Size`) |
| `SolidSyslogFileBlockDevice.h` | System setup code wiring a file-backed block device | `SolidSyslogFileBlockDeviceStorage`, `SOLIDSYSLOG_FILEBLOCKDEVICE_STORAGE_SIZE`, `SolidSyslogFileBlockDevice_Create(storage, readFile, writeFile, pathPrefix)` (sequence-numbered filenames `<prefix><NN>.log`, two cached file handles), `_Destroy(device)` |
| `SolidSyslogBlockStore.h` | System setup code using a BlockDevice-backed store | `SolidSyslogBlockStoreStorage`, `SOLIDSYSLOG_BLOCKSTORE_STORAGE_SIZE`, `SolidSyslogBlockStoreConfig` (with `blockDevice`, `storeFullContext`, `getCapacityThreshold`, `onThresholdCrossed`, `thresholdContext`), `SolidSyslogBlockStore_Create(storage, config)`, `_Destroy(store)`, `SolidSyslogDiscardPolicy` (`SolidSyslogDiscardPolicy_Oldest` / `_Newest` / `_Halt`), `SolidSyslogStoreFullCallback(void* context)`, `SolidSyslogStoreThresholdFunction(void* context)` (returns threshold in bytes; 0 disables; queried each Write), `SolidSyslogStoreThresholdCallback(void* context)` (edge-triggered; NullBuffer recursion gotcha — see header) |
| `SolidSyslogBlockStore.h` | System setup code using a BlockDevice-backed store | `SolidSyslogBlockStoreStorage`, `SOLIDSYSLOG_BLOCKSTORE_STORAGE_SIZE`, `SolidSyslogBlockStoreConfig` (with `blockDevice`, `storeFullContext`, `getCapacityThreshold`, `onThresholdCrossed`, `thresholdContext`), `SolidSyslogBlockStore_Create(storage, config)`, `_Destroy(store)`, `SolidSyslogDiscardPolicy` (`SolidSyslogDiscardPolicy_Oldest` / `_Newest` / `_Halt`), `SolidSyslogStoreFullCallback(void* context)`, `SolidSyslogStoreThresholdFunction(void* context)` (returns threshold in bytes; 0 disables; queried each Write), `SolidSyslogStoreThresholdCallback(void* context)` (edge-triggered; PassthroughBuffer recursion gotcha — see header) |
| `SolidSyslogSecurityPolicyDefinition.h` | SecurityPolicy implementors (extension point) | `SolidSyslogSecurityPolicy` vtable struct, `SOLIDSYSLOG_MAX_INTEGRITY_SIZE` |
| `SolidSyslogNullSecurityPolicy.h` | System setup code (no integrity checking) | `SolidSyslogNullSecurityPolicy_Create`, `_Destroy` |
| `SolidSyslogCrc16Policy.h` | System setup code using CRC-16 integrity | `SolidSyslogCrc16Policy_Create`, `_Destroy` |
Expand Down Expand Up @@ -462,8 +462,8 @@ code should follow them; reviewers should call out drift.
comparison operators.** Plain `&&` between bool-typed operands needs no extra
parens — readability wins over MISRA pedantry there.
- **No null-pointer checks where the type's null object exists.** Use
`SolidSyslogNullSecurityPolicy`, `SolidSyslogNullStore`, `SolidSyslogNullBuffer`
rather than `if (policy != NULL) policy->Compute(...)`.
`SolidSyslogNullSecurityPolicy`, `SolidSyslogNullStore` rather than
`if (policy != NULL) policy->Compute(...)`.

### Test code

Expand Down
2 changes: 1 addition & 1 deletion Core/Interface/SolidSyslogBlockStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ EXTERN_C_BEGIN
typedef size_t (*SolidSyslogStoreThresholdFunction)(void* context);

/* Edge-triggered: fires once when used-bytes transitions from below threshold to at-or-above.
* NullBuffer note: SolidSyslog_Log is synchronous under SolidSyslogNullBuffer, so calling
* PassthroughBuffer note: SolidSyslog_Log is synchronous under SolidSyslogPassthroughBuffer, so calling
* SolidSyslog_Log from this callback will recurse into Store_Write. Either gate the Log,
* or use SolidSyslogPosixMessageQueueBuffer (which returns immediately). */
typedef void (*SolidSyslogStoreThresholdCallback)(void* context);
Expand Down
15 changes: 0 additions & 15 deletions Core/Interface/SolidSyslogNullBuffer.h

This file was deleted.

15 changes: 15 additions & 0 deletions Core/Interface/SolidSyslogPassthroughBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef SOLIDSYSLOGPASSTHROUGHBUFFER_H
#define SOLIDSYSLOGPASSTHROUGHBUFFER_H

#include "ExternC.h"

EXTERN_C_BEGIN

struct SolidSyslogSender;

struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender * sender);
void SolidSyslogPassthroughBuffer_Destroy(void);

EXTERN_C_END

#endif /* SOLIDSYSLOGPASSTHROUGHBUFFER_H */
2 changes: 1 addition & 1 deletion Core/Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set(SOURCES
SolidSyslogBuffer.c
SolidSyslogFormatter.c
SolidSyslogMetaSd.c
SolidSyslogNullBuffer.c
SolidSyslogPassthroughBuffer.c
SolidSyslogCircularBuffer.c
SolidSyslogCircularBufferStatic.c
SolidSyslogPoolAllocator.c
Expand Down
5 changes: 2 additions & 3 deletions Core/Source/SolidSyslog.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,8 @@ static inline void SolidSyslog_FormatNilvalue(struct SolidSyslogFormatter* f)
* — its absence is a valid integrator choice (matches SolidSyslogNullStore
* semantics: drain proceeds, fall-through to direct send).
*
* The public SolidSyslogNull* family is for integrator-chosen no-ops with
* different semantics (e.g. NullBuffer is a direct-send shim); these
* internal nils are crash-safe defaults only.
* The public SolidSyslogNull* family is for integrator-chosen no-ops;
* these internal nils are crash-safe defaults only.
* ============================================================================ */

static bool nilBufferReportArmed = true;
Expand Down
55 changes: 0 additions & 55 deletions Core/Source/SolidSyslogNullBuffer.c

This file was deleted.

55 changes: 55 additions & 0 deletions Core/Source/SolidSyslogPassthroughBuffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "SolidSyslogPassthroughBuffer.h"

#include <stdbool.h>
#include <stddef.h>

#include "SolidSyslogBufferDefinition.h"
#include "SolidSyslogSender.h"

static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead);
static void PassthroughBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size);

static inline struct SolidSyslogPassthroughBuffer* PassthroughBuffer_SelfFromBase(struct SolidSyslogBuffer* base);

struct SolidSyslogPassthroughBuffer
{
struct SolidSyslogBuffer Base;
struct SolidSyslogSender* Sender;
};

static struct SolidSyslogPassthroughBuffer PassthroughBuffer_Instance;

struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender* sender)
{
PassthroughBuffer_Instance.Base.Write = PassthroughBuffer_Write;
PassthroughBuffer_Instance.Base.Read = PassthroughBuffer_Read;
PassthroughBuffer_Instance.Sender = sender;
return &PassthroughBuffer_Instance.Base;
}

void SolidSyslogPassthroughBuffer_Destroy(void)
{
PassthroughBuffer_Instance.Base.Write = NULL;
PassthroughBuffer_Instance.Base.Read = NULL;
PassthroughBuffer_Instance.Sender = NULL;
}

static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead)
{
(void) base;
(void) data;
(void) maxSize;
*bytesRead = 0;
return false;
}

static void PassthroughBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size)
{
struct SolidSyslogPassthroughBuffer* self = PassthroughBuffer_SelfFromBase(base);
(void) SolidSyslogSender_Send(self->Sender, data, size);
}

static inline struct SolidSyslogPassthroughBuffer* PassthroughBuffer_SelfFromBase(struct SolidSyslogBuffer* base)
{
return (struct SolidSyslogPassthroughBuffer*) base;
}
94 changes: 94 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,99 @@
# Dev Log

## 2026-05-18 — S11.03: Rename NullBuffer to PassthroughBuffer (#397)

Pure mechanical rename — the type is a passthrough that forwards
`Write` directly into an injected `SolidSyslogSender`, not a GoF Null
Object. Splitting it out from the Core sweep (S11.04) per the epic
sequencing keeps the cross-tree rename's review surface separate from
pool semantics.

### Mechanical scope

- File renames via `git mv`: `Core/Interface/SolidSyslogNullBuffer.h`,
`Core/Source/SolidSyslogNullBuffer.c`,
`Tests/SolidSyslogNullBufferTest.cpp` → corresponding
`PassthroughBuffer` paths.
- Tier 1 symbols: `SolidSyslogNullBuffer_Create`, `_Destroy`, `struct
SolidSyslogNullBuffer`, include guard `SOLIDSYSLOGNULLBUFFER_H`.
- Tier 2 statics: `NullBuffer_Read`, `_Write`, `_SelfFromBase`,
`_Instance` → `PassthroughBuffer_*` per the strip-only rule (file
basename minus the `SolidSyslog` namespace).
- Test group: `TEST_GROUP(SolidSyslogPassthroughBuffer)`. Individual
test names left alone — they describe behaviour, not the class.
- Call sites: `Tests/SolidSyslogTest.cpp` (include + two Create/Destroy
calls); two CMakeLists.txt source-list entries.
- Doc / comment sweep: `CLAUDE.md` (audience table row), `README.md`
(two prose), `SKILL.md` (one prose), `docs/iec62443.md` (three SR
rows), `Core/Interface/SolidSyslogBlockStore.h` (recursion-gotcha
doc comment), `Bdd/features/tcp_singletask.feature`,
`Bdd/features/steps/syslog_steps.py`.
- `misra_suppressions.txt`: 11.3 entry path updated (line number
unchanged — same file content under new name).

### Doc honesty corrections (adjacent to the rename, not the rename itself)

Three places held a "but NullBuffer doesn't actually fit this rule"
caveat — the rename removes the misleading example, so the caveat
can go too. Calling these out explicitly because they're substantive
edits in a story scoped as mechanical:

- **`CLAUDE.md` Design Patterns null-object bullet** previously listed
`SolidSyslogNullBuffer` alongside `NullSecurityPolicy` and `NullStore`
as "the type's null object" sites where guard checks should be
skipped. `PassthroughBuffer` is not a null object. Removed from the
list (decided pre-raise).
- **`SKILL.md` Architecture line** previously read "Null object pattern
throughout (NullBuffer is the buffer null object)". The parenthetical
was wrong; struck. The remaining "Null object pattern throughout" is
still accurate (NullMutex / NullStore / NullSecurityPolicy genuinely
are no-ops).
- **`Core/Source/SolidSyslog.c` nil-collaborators comment block**
previously read "The public SolidSyslogNull* family is for
integrator-chosen no-ops with different semantics (e.g. NullBuffer
is a direct-send shim)". After the rename the family no longer
contains the "different semantics" example; the caveat now misleads
in the opposite direction, so dropped. The remaining sentence stands
unchanged.

### Deliberate non-renames

Three callouts so the next sed sweep doesn't catch them:

- `Tests/SolidSyslogTest.cpp::TEST(SolidSyslogLifecycle, CreateWithNullBufferReportsError)` —
refers to NULL pointer (`config.Buffer = NULL`), not the class.
- `Tests/SolidSyslogUdpSenderTest.cpp::TEST(SolidSyslogUdpSenderBadSetup,
SendWithNullBufferReportsErrorAndDoesNotSend)` — same.
- `Bdd/Targets/FreeRtos/main.c:6` historical comment about S08.04 —
preserved as historical narrative; at the time the type really was
called NullBuffer.

### Gates

debug, clang-debug, sanitize, coverage, tidy, cppcheck, clang-format.
cppcheck-misra count **88 on the rename branch, 88 on main** — zero
new findings, zero entries against `SolidSyslogPassthroughBuffer.c`.
(Side-note: my S11.02 closing note recorded "60" as the baseline; that
appears to have been a non-deterministic cppcheck run or an
incomplete invocation — the actual main baseline at the time of this
session is 88, verified by running the identical command against main
in isolation. AC #3 in the story body inherits the same stale number;
the true invariant — "no new findings introduced by the rename" — is
met.)

PassthroughBuffer.c at 100% line coverage post-rename.

### Out of scope (carrying forward to S11.04)

The `Instance` singleton, the `void _Destroy(void)` signature, the
existing 11.3 storage-cast on the `SelfFromBase` helper, and the
absence of a `SOLIDSYSLOG_PASSTHROUGHBUFFER_POOL_SIZE` tunable all
carry forward unchanged. S11.04 (Core sweep) migrates this class
onto `SolidSyslogPoolAllocator` and retires the cast in the same
move.

---

## 2026-05-18 — S11.02: Extract SolidSyslogPoolAllocator helper (#395)

S11.01 left every E11 class about to copy the same 13-function pool
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ is not a core dependency; Core has zero OpenSSL references.
Designed for resource-constrained environments:
- C99, no dynamic memory allocation required — allocator is caller-injected
- Transport-agnostic — UDP, TCP, TLS, or bring your own
- Buffer-agnostic — NullBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own
- Buffer-agnostic — PassthroughBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own
- No `#ifdef` feature flags — optional features composed at link time
- MISRA C:2012 informed
- Dependency injection throughout — fully testable without a network
Expand Down Expand Up @@ -61,7 +61,7 @@ Public headers are split by audience (Interface Segregation Principle):
- **`SolidSyslogError.h`** — install a handler to react to library-internal errors (NULL guards, send failures); default is silent. See `Bdd/Targets/Common/BddTargetStderrErrorHandler.c` for a reference implementation
- **`SolidSyslogConfigLock.h`** — optional setup-time lock injection (`SolidSyslog_SetConfigLock(lockFn, unlockFn)`); wraps library-internal pool slot walks so multi-task setup is safe. Defaults are no-ops, so single-task systems can ignore it. Integrators on RTOS / multi-core wire `taskENTER_CRITICAL`, a static `pthread_mutex_t`, `EnterCriticalSection`, or a spinlock pair
- **`SolidSyslogSenderDefinition.h`** / **`SolidSyslogBufferDefinition.h`** — extension points for custom senders and buffers
- **`SolidSyslogNullBuffer.h`** — direct-send buffer for single-task systems
- **`SolidSyslogPassthroughBuffer.h`** — direct-send buffer for single-task systems
- **`SolidSyslogCircularBuffer.h`** — portable ring buffer with caller-allocated ring memory and an injected `SolidSyslogMutex` (`SolidSyslogPosixMutex` / `SolidSyslogWindowsMutex` / `SolidSyslogFreeRtosMutex` / `SolidSyslogNullMutex` / your own); the cross-platform threaded buffer. Instance bookkeeping lives in a library-internal static pool (E11)
- **`SolidSyslogPosixMessageQueueBuffer.h`** — thread-safe POSIX message queue buffer
- **`SolidSyslogUdpSender.h`** — UDP transport (RFC 5426)
Expand Down
4 changes: 2 additions & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ Test progression follows ZOMBIES order.
- OO-in-C: structs with function pointers (vtable pattern)
- Dependency injection for transport, buffering, clock, hostname, allocator
- Buffer abstraction decouples formatting from sending: `SolidSyslog_Log` writes to buffer,
`SolidSyslog_Service` reads from buffer and sends. Implementations: NullBuffer (direct send,
`SolidSyslog_Service` reads from buffer and sends. Implementations: PassthroughBuffer (direct send,
single-task), CircularBuffer (portable ring with caller-allocated ring memory and an injected
`SolidSyslogMutex` — Posix/Windows/Null mutex shipped, integrators can plug their own RTOS
primitive), PosixMessageQueueBuffer (POSIX message queue, used by the Linux Threaded example)
- Null object pattern throughout (NullBuffer is the buffer null object)
- Null object pattern throughout
- All fields use uniform field object pattern with format function pointer
- Optional features composed at link time — no conditional compilation
- C11 static assertions via compatibility shim
Expand Down
Loading
Loading