diff --git a/Bdd/Targets/Common/BddTargetAppName.c b/Bdd/Targets/Common/BddTargetAppName.c index 5505e9d1..06ec56c9 100644 --- a/Bdd/Targets/Common/BddTargetAppName.c +++ b/Bdd/Targets/Common/BddTargetAppName.c @@ -1,5 +1,5 @@ #include "BddTargetAppName.h" -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include #include @@ -57,7 +57,8 @@ void BddTargetAppName_Set(const char* argv0) } } -void BddTargetAppName_Get(struct SolidSyslogFormatter* formatter) +void BddTargetAppName_Get(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_BoundedString(formatter, appName, appNameLength); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, appName, appNameLength); } diff --git a/Bdd/Targets/Common/BddTargetAppName.h b/Bdd/Targets/Common/BddTargetAppName.h index 1da4b2af..75d312da 100644 --- a/Bdd/Targets/Common/BddTargetAppName.h +++ b/Bdd/Targets/Common/BddTargetAppName.h @@ -5,10 +5,10 @@ EXTERN_C_BEGIN - struct SolidSyslogFormatter; + struct SolidSyslogHeaderField; void BddTargetAppName_Set(const char* argv0); - void BddTargetAppName_Get(struct SolidSyslogFormatter * formatter); + void BddTargetAppName_Get(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Bdd/Targets/Common/BddTargetFreeRtosPipeline.c b/Bdd/Targets/Common/BddTargetFreeRtosPipeline.c index a45485ec..14003347 100644 --- a/Bdd/Targets/Common/BddTargetFreeRtosPipeline.c +++ b/Bdd/Targets/Common/BddTargetFreeRtosPipeline.c @@ -27,6 +27,7 @@ #include "SolidSyslogError.h" #include "SolidSyslogFileBlockDevice.h" #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include "SolidSyslogFreeRtosMutex.h" #include "SolidSyslogFreeRtosSysUpTime.h" #include "SolidSyslogMbedTlsAesGcmPolicy.h" @@ -175,7 +176,7 @@ static void OnStoreFull(void* context); static size_t GetCapacityThreshold(void* context); static void OnThresholdCrossed(void* context); static void TeardownAll(void); -static void GetAppName(struct SolidSyslogFormatter* formatter); +static void GetAppName(struct SolidSyslogHeaderField* field, void* context); static void GetTimeQuality(struct SolidSyslogTimeQuality* timeQuality); static void ErrorHandlerEx(void* context, const struct SolidSyslogErrorEvent* event); @@ -220,9 +221,10 @@ void BddTargetFreeRtosPipeline_SetConfig(const struct BddTargetFreeRtosPipelineC /* ---- SolidSyslog config callbacks ------------------------------------------ */ -static void GetAppName(struct SolidSyslogFormatter* formatter) +static void GetAppName(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_BoundedString(formatter, appName, strlen(appName)); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, appName, strlen(appName)); } /* No RTC and no time-sync on these reference targets — RFC 5424 §6.2.3.1 diff --git a/Bdd/Targets/Common/BddTargetFreeRtosPipeline.h b/Bdd/Targets/Common/BddTargetFreeRtosPipeline.h index ef3f08f3..c3865983 100644 --- a/Bdd/Targets/Common/BddTargetFreeRtosPipeline.h +++ b/Bdd/Targets/Common/BddTargetFreeRtosPipeline.h @@ -2,8 +2,8 @@ #define BDD_TARGET_FREE_RTOS_PIPELINE_H #include "SolidSyslogEndpoint.h" +#include "SolidSyslogHeaderFieldFunction.h" #include "SolidSyslogSender.h" -#include "SolidSyslogStringFunction.h" #include #include @@ -37,7 +37,7 @@ struct BddTargetFreeRtosPipelineConfig struct SolidSyslogSender* (*BuildSender)(void); /* Emit the RFC 5424 HOSTNAME by reading the platform IP stack. Wired into * SolidSyslogConfig.GetHostname. */ - SolidSyslogStringFunction GetHostname; + SolidSyslogHeaderFieldFunction GetHostname; /* Tear down the sender + platform adapters. Runs on the interactive task * after the shared pipeline teardown (SolidSyslog / SD / store / buffer). */ void (*TeardownNetwork)(void); diff --git a/Bdd/Targets/FreeRtos/main.c b/Bdd/Targets/FreeRtos/main.c index fbdd8d61..1eede1c4 100644 --- a/Bdd/Targets/FreeRtos/main.c +++ b/Bdd/Targets/FreeRtos/main.c @@ -23,7 +23,7 @@ #include "BddTargetTlsConfig.h" #include "BddTargetTlsSender.h" -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include "SolidSyslogPlusTcpAddress.h" #include "SolidSyslogPlusTcpDatagram.h" #include "SolidSyslogPlusTcpResolver.h" @@ -96,7 +96,7 @@ static BaseType_t interactiveTaskCreated = pdFALSE; extern NetworkInterface_t* pxMPS2_FillInterfaceDescriptor(BaseType_t xEMACIndex, NetworkInterface_t* pxInterface); static void SetEthernetIrqPriority(void); -static void GetHostname(struct SolidSyslogFormatter* formatter); +static void GetHostname(struct SolidSyslogHeaderField* field, void* context); static struct SolidSyslogSender* BuildSender(void); static void TeardownNetwork(void); @@ -229,16 +229,17 @@ static void SetEthernetIrqPriority(void) *ipr = ETHERNET_IRQ_PRIORITY; } -static void GetHostname(struct SolidSyslogFormatter* formatter) +static void GetHostname(struct SolidSyslogHeaderField* field, void* context) { /* RFC 5424 §6.2.4 rung 2 (static IP address). Read back from the IP stack so * a future DHCP / hostname slice satisfies the same rung without re-touching * this callback. */ uint32_t ipAddress = 0U; char ipBuffer[16]; + (void) context; FreeRTOS_GetEndPointConfiguration(&ipAddress, NULL, NULL, NULL, &networkEndPoint); FreeRTOS_inet_ntoa(ipAddress, ipBuffer); - SolidSyslogFormatter_BoundedString(formatter, ipBuffer, strlen(ipBuffer)); + SolidSyslogHeaderField_PrintUsAscii(field, ipBuffer, strlen(ipBuffer)); } /* Build the PlusTcp SwitchingSender: UDP datagram, octet-framed TCP, and a diff --git a/Bdd/Targets/FreeRtosLwip/main.c b/Bdd/Targets/FreeRtosLwip/main.c index 1ecca1a2..12692eb1 100644 --- a/Bdd/Targets/FreeRtosLwip/main.c +++ b/Bdd/Targets/FreeRtosLwip/main.c @@ -24,7 +24,7 @@ #include "BddTargetSwitchConfig.h" #include "BddTargetTlsSender.h" -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include "SolidSyslogLwipRawAddress.h" #include "SolidSyslogLwipRawDatagram.h" #include "SolidSyslogLwipRawDnsResolver.h" @@ -70,7 +70,7 @@ static void LwipTcpipMarshal(SolidSyslogLwipRawCallback callback, void* context) static void NetworkBringUp(void* context); static void WarmUpGatewayArp(void); static void GatewayResolvedQuery(void* context); -static void GetHostname(struct SolidSyslogFormatter* formatter); +static void GetHostname(struct SolidSyslogHeaderField* field, void* context); static struct SolidSyslogSender* BuildSender(void); static void TeardownNetwork(void); @@ -233,12 +233,13 @@ void vApplicationStackOverflowHook(TaskHandle_t task, char* taskName) } } -static void GetHostname(struct SolidSyslogFormatter* formatter) +static void GetHostname(struct SolidSyslogHeaderField* field, void* context) { /* RFC 5424 §6.2.4 rung 2 (static IP) — read back from the netif so a future * DHCP slice satisfies the same rung without touching this callback. */ const char* address = ip4addr_ntoa(netif_ip4_addr(&networkInterface)); - SolidSyslogFormatter_BoundedString(formatter, address, strlen(address)); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, address, strlen(address)); } /* Bring up the netif on the tcpip thread, warm the gateway ARP, then build the diff --git a/Core/Interface/SolidSyslogConfig.h b/Core/Interface/SolidSyslogConfig.h index a9cd3ffa..e579cdca 100644 --- a/Core/Interface/SolidSyslogConfig.h +++ b/Core/Interface/SolidSyslogConfig.h @@ -3,7 +3,7 @@ #include -#include "SolidSyslogStringFunction.h" +#include "SolidSyslogHeaderFieldFunction.h" #include "SolidSyslogTimestamp.h" #include "ExternC.h" @@ -11,7 +11,7 @@ EXTERN_C_BEGIN struct SolidSyslog; struct SolidSyslogBuffer; - struct SolidSyslogFormatter; + struct SolidSyslogHeaderField; struct SolidSyslogSender; struct SolidSyslogStore; struct SolidSyslogStructuredData; @@ -21,9 +21,12 @@ EXTERN_C_BEGIN struct SolidSyslogBuffer* Buffer; struct SolidSyslogSender* Sender; SolidSyslogClockFunction Clock; - SolidSyslogStringFunction GetHostname; - SolidSyslogStringFunction GetAppName; - SolidSyslogStringFunction GetProcessId; + SolidSyslogHeaderFieldFunction GetHostname; + void* GetHostnameContext; + SolidSyslogHeaderFieldFunction GetAppName; + void* GetAppNameContext; + SolidSyslogHeaderFieldFunction GetProcessId; + void* GetProcessIdContext; struct SolidSyslogStore* Store; struct SolidSyslogStructuredData** Sd; size_t SdCount; diff --git a/Core/Interface/SolidSyslogHeaderField.h b/Core/Interface/SolidSyslogHeaderField.h new file mode 100644 index 00000000..a2942a15 --- /dev/null +++ b/Core/Interface/SolidSyslogHeaderField.h @@ -0,0 +1,34 @@ +#ifndef SOLIDSYSLOGHEADERFIELD_H +#define SOLIDSYSLOGHEADERFIELD_H + +#include "ExternC.h" + +#include +#include + +EXTERN_C_BEGIN + + /* The value sink for an RFC 5424 header field (HOSTNAME / APP-NAME / + * PROCID). A field producer is handed only a SolidSyslogHeaderField* — it + * can append PRINTUSASCII content (printable US-ASCII; any other byte, + * space included, is substituted) bounded to the field width, but cannot + * reach the raw formatter. Stack-transient, no pool (D.002). */ + struct SolidSyslogHeaderField; + + /* Appends up to maxLength bytes of source (stopping at a NUL terminator), + * substituting any byte outside printable US-ASCII ('!'..'~') — space + * included — with the library's substitute character. Output is further + * bounded by the field width the writer was created with. */ + void SolidSyslogHeaderField_PrintUsAscii( + struct SolidSyslogHeaderField * field, + const char* source, + size_t maxLength + ); + + /* Appends the decimal digits of value (always printable US-ASCII), bounded + * by the field width. */ + void SolidSyslogHeaderField_Uint32(struct SolidSyslogHeaderField * field, uint32_t value); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGHEADERFIELD_H */ diff --git a/Core/Interface/SolidSyslogHeaderFieldFunction.h b/Core/Interface/SolidSyslogHeaderFieldFunction.h new file mode 100644 index 00000000..26c87d71 --- /dev/null +++ b/Core/Interface/SolidSyslogHeaderFieldFunction.h @@ -0,0 +1,19 @@ +#ifndef SOLIDSYSLOGHEADERFIELDFUNCTION_H +#define SOLIDSYSLOGHEADERFIELDFUNCTION_H + +#include "ExternC.h" + +EXTERN_C_BEGIN + + struct SolidSyslogHeaderField; + + /* Appends an RFC 5424 header-field value (HOSTNAME / APP-NAME / PROCID) into + * the field sink it is handed. The library owns the PRINTUSASCII charset + * (the sink applies it), so a callback cannot break the header framing. + * context is passed through unchanged from the config the callback was + * registered in. */ + typedef void (*SolidSyslogHeaderFieldFunction)(struct SolidSyslogHeaderField* field, void* context); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGHEADERFIELDFUNCTION_H */ diff --git a/Core/Interface/SolidSyslogStringFunction.h b/Core/Interface/SolidSyslogStringFunction.h deleted file mode 100644 index 597a1644..00000000 --- a/Core/Interface/SolidSyslogStringFunction.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SOLIDSYSLOGSTRINGFUNCTION_H -#define SOLIDSYSLOGSTRINGFUNCTION_H - -#include "ExternC.h" - -EXTERN_C_BEGIN - - struct SolidSyslogFormatter; - - typedef void (*SolidSyslogStringFunction)(struct SolidSyslogFormatter* formatter); - -EXTERN_C_END - -#endif /* SOLIDSYSLOGSTRINGFUNCTION_H */ diff --git a/Core/Source/CMakeLists.txt b/Core/Source/CMakeLists.txt index 235f9460..2a84eb12 100644 --- a/Core/Source/CMakeLists.txt +++ b/Core/Source/CMakeLists.txt @@ -34,6 +34,7 @@ set(SOURCES SolidSyslogStructuredData.c SolidSyslogSdValue.c SolidSyslogSdElement.c + SolidSyslogHeaderField.c SolidSyslogTimeQualitySd.c SolidSyslogTimeQualitySdStatic.c SolidSyslogOriginSd.c diff --git a/Core/Source/SolidSyslog.c b/Core/Source/SolidSyslog.c index 6e70e8b5..b239eed6 100644 --- a/Core/Source/SolidSyslog.c +++ b/Core/Source/SolidSyslog.c @@ -15,27 +15,39 @@ #include "SolidSyslogNullSender.h" #include "SolidSyslogNullStore.h" #include "SolidSyslogPrivate.h" +#include "SolidSyslogHeaderFieldFunction.h" #include "SolidSyslogSender.h" #include "SolidSyslogStore.h" -#include "SolidSyslogStringFunction.h" #include "SolidSyslogTimestamp.h" #include "SolidSyslogTunables.h" const struct SolidSyslogErrorSource SolidSyslogErrorSource = {"SolidSyslog"}; struct SolidSyslogBuffer; -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; struct SolidSyslogSender; struct SolidSyslogStore; struct SolidSyslogStructuredData; static inline void SolidSyslog_DrainBufferIntoStore(struct SolidSyslog* self); static inline void SolidSyslog_SendOneFromStore(struct SolidSyslog* self); -static void SolidSyslog_InstallAppName(struct SolidSyslog* self, SolidSyslogStringFunction configured); +static void SolidSyslog_InstallAppName( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +); static void SolidSyslog_InstallBuffer(struct SolidSyslog* self, struct SolidSyslogBuffer* configured); static void SolidSyslog_InstallClock(struct SolidSyslog* self, SolidSyslogClockFunction configured); -static void SolidSyslog_InstallHostname(struct SolidSyslog* self, SolidSyslogStringFunction configured); -static void SolidSyslog_InstallProcessId(struct SolidSyslog* self, SolidSyslogStringFunction configured); +static void SolidSyslog_InstallHostname( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +); +static void SolidSyslog_InstallProcessId( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +); static void SolidSyslog_InstallSender(struct SolidSyslog* self, struct SolidSyslogSender* configured); static void SolidSyslog_InstallStore(struct SolidSyslog* self, struct SolidSyslogStore* configured); static void SolidSyslog_InstallStructuredData( @@ -54,9 +66,9 @@ void SolidSyslog_Initialise(struct SolidSyslog* self, const struct SolidSyslogCo SolidSyslog_InstallSender(self, config->Sender); SolidSyslog_InstallStore(self, config->Store); SolidSyslog_InstallClock(self, config->Clock); - SolidSyslog_InstallHostname(self, config->GetHostname); - SolidSyslog_InstallAppName(self, config->GetAppName); - SolidSyslog_InstallProcessId(self, config->GetProcessId); + SolidSyslog_InstallHostname(self, config->GetHostname, config->GetHostnameContext); + SolidSyslog_InstallAppName(self, config->GetAppName, config->GetAppNameContext); + SolidSyslog_InstallProcessId(self, config->GetProcessId, config->GetProcessIdContext); SolidSyslog_InstallStructuredData(self, config->Sd, config->SdCount); } @@ -74,9 +86,12 @@ static void SolidSyslog_ResetToDefaults(struct SolidSyslog* self) self->Sender = SolidSyslogNullSender_Get(); self->Store = SolidSyslogNullStore_Get(); self->Format.Clock = SolidSyslog_NullClock; - self->Format.GetHostname = SolidSyslog_NullStringFunction; - self->Format.GetAppName = SolidSyslog_NullStringFunction; - self->Format.GetProcessId = SolidSyslog_NullStringFunction; + self->Format.GetHostname = SolidSyslog_NullHeaderField; + self->Format.GetHostnameContext = NULL; + self->Format.GetAppName = SolidSyslog_NullHeaderField; + self->Format.GetAppNameContext = NULL; + self->Format.GetProcessId = SolidSyslog_NullHeaderField; + self->Format.GetProcessIdContext = NULL; self->Format.Sd = NULL; self->Format.SdCount = 0; } @@ -137,27 +152,42 @@ static void SolidSyslog_InstallClock(struct SolidSyslog* self, SolidSyslogClockF } } -static void SolidSyslog_InstallHostname(struct SolidSyslog* self, SolidSyslogStringFunction configured) +static void SolidSyslog_InstallHostname( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +) { if (configured != NULL) { self->Format.GetHostname = configured; + self->Format.GetHostnameContext = context; } } -static void SolidSyslog_InstallAppName(struct SolidSyslog* self, SolidSyslogStringFunction configured) +static void SolidSyslog_InstallAppName( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +) { if (configured != NULL) { self->Format.GetAppName = configured; + self->Format.GetAppNameContext = context; } } -static void SolidSyslog_InstallProcessId(struct SolidSyslog* self, SolidSyslogStringFunction configured) +static void SolidSyslog_InstallProcessId( + struct SolidSyslog* self, + SolidSyslogHeaderFieldFunction configured, + void* context +) { if (configured != NULL) { self->Format.GetProcessId = configured; + self->Format.GetProcessIdContext = context; } } @@ -288,7 +318,8 @@ void SolidSyslog_NullClock(struct SolidSyslogTimestamp* ts) (void) ts; } -void SolidSyslog_NullStringFunction(struct SolidSyslogFormatter* formatter) +void SolidSyslog_NullHeaderField(struct SolidSyslogHeaderField* field, void* context) { - (void) formatter; + (void) field; + (void) context; } diff --git a/Core/Source/SolidSyslogHeaderField.c b/Core/Source/SolidSyslogHeaderField.c new file mode 100644 index 00000000..3ab2aaba --- /dev/null +++ b/Core/Source/SolidSyslogHeaderField.c @@ -0,0 +1,48 @@ +#include "SolidSyslogHeaderFieldPrivate.h" + +#include + +#include "SolidSyslogFormatter.h" + +static inline size_t HeaderField_Consumed(const struct SolidSyslogHeaderField* field, size_t before); + +void SolidSyslogHeaderField_FromFormatter( + struct SolidSyslogHeaderField* field, + struct SolidSyslogFormatter* formatter, + size_t maxLength +) +{ + field->Formatter = formatter; + field->Remaining = maxLength; +} + +void SolidSyslogHeaderField_PrintUsAscii(struct SolidSyslogHeaderField* field, const char* source, size_t maxLength) +{ + size_t limit = (maxLength < field->Remaining) ? maxLength : field->Remaining; + size_t before = SolidSyslogFormatter_Length(field->Formatter); + + SolidSyslogFormatter_PrintUsAsciiString(field->Formatter, source, limit); + field->Remaining -= HeaderField_Consumed(field, before); +} + +void SolidSyslogHeaderField_Uint32(struct SolidSyslogHeaderField* field, uint32_t value) +{ + /* Unlike _PrintUsAscii, _Uint32 cannot pre-clamp its output to the field + * budget (the formatter writes the whole number), so guard the budget + * directly: skip once the field is full, and clamp to zero rather than + * letting the size_t subtraction underflow when this number alone overruns + * the remainder. A header-field callback may mix any number of appends. */ + if (field->Remaining > 0U) + { + size_t before = SolidSyslogFormatter_Length(field->Formatter); + + SolidSyslogFormatter_Uint32(field->Formatter, value); + size_t consumed = HeaderField_Consumed(field, before); + field->Remaining = (consumed >= field->Remaining) ? 0U : (field->Remaining - consumed); + } +} + +static inline size_t HeaderField_Consumed(const struct SolidSyslogHeaderField* field, size_t before) +{ + return SolidSyslogFormatter_Length(field->Formatter) - before; +} diff --git a/Core/Source/SolidSyslogHeaderFieldPrivate.h b/Core/Source/SolidSyslogHeaderFieldPrivate.h new file mode 100644 index 00000000..df5ef3a3 --- /dev/null +++ b/Core/Source/SolidSyslogHeaderFieldPrivate.h @@ -0,0 +1,35 @@ +#ifndef SOLIDSYSLOGHEADERFIELDPRIVATE_H +#define SOLIDSYSLOGHEADERFIELDPRIVATE_H + +#include "ExternC.h" + +#include + +#include "SolidSyslogHeaderField.h" + +EXTERN_C_BEGIN + + struct SolidSyslogFormatter; + + /* Definition lives here (not the public header) so a field producer handed + * a SolidSyslogHeaderField* cannot reach the wrapped formatter. Remaining + * is the field-width budget still available across this field's appends. */ + struct SolidSyslogHeaderField + { + struct SolidSyslogFormatter* Formatter; + size_t Remaining; + }; + + /* Internal constructor — wraps a message-buffer formatter and caps this + * field at maxLength bytes. MessageFormatter builds one per header field + * and passes it to the configured callback. Stack-transient: the caller + * owns the storage. */ + void SolidSyslogHeaderField_FromFormatter( + struct SolidSyslogHeaderField * field, + struct SolidSyslogFormatter * formatter, + size_t maxLength + ); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGHEADERFIELDPRIVATE_H */ diff --git a/Core/Source/SolidSyslogMessageFormatter.c b/Core/Source/SolidSyslogMessageFormatter.c index a6bd67d3..d71bff8e 100644 --- a/Core/Source/SolidSyslogMessageFormatter.c +++ b/Core/Source/SolidSyslogMessageFormatter.c @@ -6,9 +6,10 @@ #include "SolidSyslog.h" #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderFieldFunction.h" +#include "SolidSyslogHeaderFieldPrivate.h" #include "SolidSyslogPrival.h" #include "SolidSyslogSdElementPrivate.h" -#include "SolidSyslogStringFunction.h" #include "SolidSyslogStructuredData.h" #include "SolidSyslogTimestamp.h" #include "SolidSyslogTimestampFormatter.h" @@ -33,7 +34,8 @@ static inline bool MessageFormatter_SeverityIsValid(uint8_t severity); static inline void MessageFormatter_FormatTimestamp(struct SolidSyslogFormatter* f, SolidSyslogClockFunction clock); static inline void MessageFormatter_FormatStringField( struct SolidSyslogFormatter* f, - SolidSyslogStringFunction fn, + SolidSyslogHeaderFieldFunction fn, + void* context, size_t maxSize ); static inline void MessageFormatter_FormatMsgId(struct SolidSyslogFormatter* f, const char* messageId); @@ -57,11 +59,26 @@ void SolidSyslogMessageFormatter_Format( SolidSyslogFormatter_AsciiCharacter(f, ' '); MessageFormatter_FormatTimestamp(f, context->Clock); SolidSyslogFormatter_AsciiCharacter(f, ' '); - MessageFormatter_FormatStringField(f, context->GetHostname, SOLIDSYSLOG_MAX_HOSTNAME_SIZE); + MessageFormatter_FormatStringField( + f, + context->GetHostname, + context->GetHostnameContext, + SOLIDSYSLOG_MAX_HOSTNAME_SIZE + ); SolidSyslogFormatter_AsciiCharacter(f, ' '); - MessageFormatter_FormatStringField(f, context->GetAppName, SOLIDSYSLOG_MAX_APP_NAME_SIZE); + MessageFormatter_FormatStringField( + f, + context->GetAppName, + context->GetAppNameContext, + SOLIDSYSLOG_MAX_APP_NAME_SIZE + ); SolidSyslogFormatter_AsciiCharacter(f, ' '); - MessageFormatter_FormatStringField(f, context->GetProcessId, SOLIDSYSLOG_MAX_PROCESS_ID_SIZE); + MessageFormatter_FormatStringField( + f, + context->GetProcessId, + context->GetProcessIdContext, + SOLIDSYSLOG_MAX_PROCESS_ID_SIZE + ); SolidSyslogFormatter_AsciiCharacter(f, ' '); MessageFormatter_FormatMsgId(f, message->MessageId); SolidSyslogFormatter_AsciiCharacter(f, ' '); @@ -121,22 +138,21 @@ static inline void MessageFormatter_FormatTimestamp(struct SolidSyslogFormatter* static inline void MessageFormatter_FormatStringField( struct SolidSyslogFormatter* f, - SolidSyslogStringFunction fn, + SolidSyslogHeaderFieldFunction fn, + void* context, size_t maxSize ) { - SolidSyslogFormatterStorage fieldStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(SOLIDSYSLOG_MAX_HOSTNAME_SIZE)]; - struct SolidSyslogFormatter* field = SolidSyslogFormatter_Create(fieldStorage, maxSize); - - fn(field); + size_t lengthBefore = SolidSyslogFormatter_Length(f); + struct SolidSyslogHeaderField field; - size_t fieldLength = SolidSyslogFormatter_Length(field); + /* maxSize is the field's storage size (carries a NUL slot); the usable + * field width is one less — matching the RFC HOSTNAME / APP-NAME / PROCID + * caps the scratch-field formatter enforced before this writer existed. */ + SolidSyslogHeaderField_FromFormatter(&field, f, maxSize - 1U); + fn(&field, context); - if (fieldLength > 0U) - { - SolidSyslogFormatter_PrintUsAsciiString(f, SolidSyslogFormatter_AsFormattedBuffer(field), fieldLength); - } - else + if (SolidSyslogFormatter_Length(f) == lengthBefore) { SolidSyslogFormatter_NilValue(f); } diff --git a/Core/Source/SolidSyslogMessageFormatter.h b/Core/Source/SolidSyslogMessageFormatter.h index 5da2f004..a1ff6e09 100644 --- a/Core/Source/SolidSyslogMessageFormatter.h +++ b/Core/Source/SolidSyslogMessageFormatter.h @@ -5,7 +5,7 @@ #include -#include "SolidSyslogStringFunction.h" +#include "SolidSyslogHeaderFieldFunction.h" #include "SolidSyslogTimestamp.h" EXTERN_C_BEGIN @@ -20,9 +20,12 @@ EXTERN_C_BEGIN struct SolidSyslogMessageFormatterContext { SolidSyslogClockFunction Clock; - SolidSyslogStringFunction GetHostname; - SolidSyslogStringFunction GetAppName; - SolidSyslogStringFunction GetProcessId; + SolidSyslogHeaderFieldFunction GetHostname; + void* GetHostnameContext; + SolidSyslogHeaderFieldFunction GetAppName; + void* GetAppNameContext; + SolidSyslogHeaderFieldFunction GetProcessId; + void* GetProcessIdContext; struct SolidSyslogStructuredData** Sd; size_t SdCount; }; diff --git a/Core/Source/SolidSyslogPrivate.h b/Core/Source/SolidSyslogPrivate.h index 66f56653..ce95d061 100644 --- a/Core/Source/SolidSyslogPrivate.h +++ b/Core/Source/SolidSyslogPrivate.h @@ -9,7 +9,7 @@ struct SolidSyslogBuffer; struct SolidSyslogConfig; -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; struct SolidSyslogSender; struct SolidSyslogStore; @@ -29,7 +29,7 @@ void SolidSyslog_Cleanup(struct SolidSyslog* self); * NullInstance). No public Null equivalent exists for the function-pointer * typedefs, so they stay TU-internal across this class. */ void SolidSyslog_NullClock(struct SolidSyslogTimestamp* ts); -void SolidSyslog_NullStringFunction(struct SolidSyslogFormatter* formatter); +void SolidSyslog_NullHeaderField(struct SolidSyslogHeaderField* field, void* context); static inline void SolidSyslog_Report(enum SolidSyslogSeverity severity, uint16_t category, enum SolidSyslogErrors code) { diff --git a/Core/Source/SolidSyslogStatic.c b/Core/Source/SolidSyslogStatic.c index 89b7abb7..e16a777d 100644 --- a/Core/Source/SolidSyslogStatic.c +++ b/Core/Source/SolidSyslogStatic.c @@ -72,9 +72,12 @@ static void SolidSyslog_EnsureNullInstancePopulated(void) SolidSyslog_NullInstance.Sender = SolidSyslogNullSender_Get(); SolidSyslog_NullInstance.Store = SolidSyslogNullStore_Get(); SolidSyslog_NullInstance.Format.Clock = SolidSyslog_NullClock; - SolidSyslog_NullInstance.Format.GetHostname = SolidSyslog_NullStringFunction; - SolidSyslog_NullInstance.Format.GetAppName = SolidSyslog_NullStringFunction; - SolidSyslog_NullInstance.Format.GetProcessId = SolidSyslog_NullStringFunction; + SolidSyslog_NullInstance.Format.GetHostname = SolidSyslog_NullHeaderField; + SolidSyslog_NullInstance.Format.GetHostnameContext = NULL; + SolidSyslog_NullInstance.Format.GetAppName = SolidSyslog_NullHeaderField; + SolidSyslog_NullInstance.Format.GetAppNameContext = NULL; + SolidSyslog_NullInstance.Format.GetProcessId = SolidSyslog_NullHeaderField; + SolidSyslog_NullInstance.Format.GetProcessIdContext = NULL; SolidSyslog_NullInstance.Format.Sd = NULL; SolidSyslog_NullInstance.Format.SdCount = 0; populated = true; diff --git a/DEVLOG.md b/DEVLOG.md index 275ca80c..b4680980 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -14551,3 +14551,56 @@ MISRA rule — different category, doesn't set precedent. ### Open questions - None outstanding. + +## 2026-06-06 — S14.07: move header-field callbacks off the formatter + +### Decisions +- **New `SolidSyslogHeaderField` writer** (TDD'd first, 10 tests): opaque, + stack-transient US-ASCII sink for the RFC 5424 HOSTNAME / APP-NAME / PROCID + fields. Wraps the message-buffer formatter; exposes `_PrintUsAscii(source, + maxLength)` (printable-US-ASCII filter, space substituted, NUL-stopped, bounded + by maxLength AND the field width) and `_Uint32` (PROCID). A different charset + rule from SD escaping → its own type, not `SolidSyslogSdValue`. Name chosen by + David over `SolidSyslogToken` / `SolidSyslogField`. +- **Callbacks flipped** `SolidSyslogStringFunction` → `SolidSyslogHeaderFieldFunction` + (`void(SolidSyslogHeaderField*, void* context)`) in its own header, with three + paired context fields on `SolidSyslogConfig` + the MessageFormatter context. + MessageFormatter builds one HeaderField per field (cap = field-storage size − 1, + preserving the old scratch-formatter's null-slot width) and passes it; the + scratch-field + re-filter is gone. Byte-identical (existing tests stand). +- **`SolidSyslogStringFunction.h` retired** — it was the last public consumer of + `SolidSyslogFormatter`, clearing the way for S14.08 to make the formatter + fully private. +- **Surprises:** (1) `PosixMessageQueueBuffer` coincidentally reused the PROCID + helper to stamp its queue name with the pid — decoupled to a direct `getpid()` + + `Formatter_Uint32` rather than route a queue name through the header-field + helper (which would have forced a Platform source to include a Core/Source + private header). (2) ~16 positional `SolidSyslogConfig{}` test literals + silently misaligned once three context fields were interleaved (`store` landing + in a `void*` context slot) — fixed by inserting the context nullptrs. +- Migrated the Posix/Windows hostname & processId helpers + all BDD header-field + callbacks (BddTargetAppName, FreeRtos/FreeRtosLwip GetHostname, pipeline + GetAppName). Updated the two stale `docs/rfc-compliance.md` `language` + references (already wrong since S14.04) to `SolidSyslogSdValueFunction`. +- Checks: debug green (1469 tests), BddTargetTests 66/66; clang-format applied; + cppcheck-MISRA exit 0 (anchors moved: SolidSyslog.c 11.8 ×8, MessageFormatter.c + 11.8 ×5 + 5.7 20→21; renumber clean). + +### Deferred +- Moving `SolidSyslogFormatter.h` out of the public interface is S14.08, along + with the wholesale CLAUDE.md public-header-table revision (the new HeaderField / + HeaderFieldFunction rows land there). + +### Open questions +- None outstanding. + +### S14.07 follow-up (CodeRabbit review) +- `SolidSyslogHeaderField_Uint32` could underflow `Remaining` (size_t) if a number + overran the field budget. Initially looked unreachable (PROCID = a single ≤10-digit + uint32 ≤ 128 cap), but the header-field callbacks are arbitrary user code that may make + any number of mixed `_PrintUsAscii` / `_Uint32` appends on one field — so the budget must + hold across calls and `_Uint32` must respect it. Added the guard (skip when the field is + full; clamp to zero instead of underflowing) + two tests for the mixed-append cap. RFC + 5424 §6 ABNF is the source of the per-field caps (HOSTNAME 255 / APP-NAME 48 / PROCID 128 + / MSGID 32), and streaming straight to the message buffer is exactly why the writer must + carry the cap explicitly now (the old scratch formatter enforced it via its capacity). diff --git a/Platform/Posix/Interface/SolidSyslogPosixHostname.h b/Platform/Posix/Interface/SolidSyslogPosixHostname.h index 44028cb1..3e0f4c68 100644 --- a/Platform/Posix/Interface/SolidSyslogPosixHostname.h +++ b/Platform/Posix/Interface/SolidSyslogPosixHostname.h @@ -3,11 +3,11 @@ #include "ExternC.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; EXTERN_C_BEGIN - void SolidSyslogPosixHostname_Get(struct SolidSyslogFormatter * formatter); + void SolidSyslogPosixHostname_Get(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Platform/Posix/Interface/SolidSyslogPosixProcessId.h b/Platform/Posix/Interface/SolidSyslogPosixProcessId.h index f557982a..deb53145 100644 --- a/Platform/Posix/Interface/SolidSyslogPosixProcessId.h +++ b/Platform/Posix/Interface/SolidSyslogPosixProcessId.h @@ -3,11 +3,11 @@ #include "ExternC.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; EXTERN_C_BEGIN - void SolidSyslogPosixProcessId_Get(struct SolidSyslogFormatter * formatter); + void SolidSyslogPosixProcessId_Get(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Platform/Posix/Source/SolidSyslogPosixHostname.c b/Platform/Posix/Source/SolidSyslogPosixHostname.c index 3a7357d1..93a49138 100644 --- a/Platform/Posix/Source/SolidSyslogPosixHostname.c +++ b/Platform/Posix/Source/SolidSyslogPosixHostname.c @@ -2,22 +2,24 @@ #include -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; enum { MAX_HOSTNAME_SIZE = 256U }; -void SolidSyslogPosixHostname_Get(struct SolidSyslogFormatter* formatter) +void SolidSyslogPosixHostname_Get(struct SolidSyslogHeaderField* field, void* context) { char hostname[MAX_HOSTNAME_SIZE]; + (void) context; + if (gethostname(hostname, sizeof(hostname)) == 0) { hostname[sizeof(hostname) - 1U] = '\0'; - SolidSyslogFormatter_PrintUsAsciiString(formatter, hostname, sizeof(hostname)); + SolidSyslogHeaderField_PrintUsAscii(field, hostname, sizeof(hostname)); } } diff --git a/Platform/Posix/Source/SolidSyslogPosixMessageQueueBuffer.c b/Platform/Posix/Source/SolidSyslogPosixMessageQueueBuffer.c index f7bc5dda..0d365bbe 100644 --- a/Platform/Posix/Source/SolidSyslogPosixMessageQueueBuffer.c +++ b/Platform/Posix/Source/SolidSyslogPosixMessageQueueBuffer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "SolidSyslogBufferCategories.h" #include "SolidSyslogBufferDefinition.h" @@ -16,7 +17,6 @@ #include "SolidSyslogNullBuffer.h" #include "SolidSyslogPosixMessageQueueBufferErrors.h" #include "SolidSyslogPosixMessageQueueBufferPrivate.h" -#include "SolidSyslogPosixProcessId.h" #include "SolidSyslogPrival.h" const struct SolidSyslogErrorSource PosixMessageQueueBufferErrorSource = {"PosixMessageQueueBuffer"}; @@ -54,7 +54,7 @@ bool PosixMessageQueueBuffer_Initialise( struct SolidSyslogFormatter* name = SolidSyslogFormatter_Create(self->NameStorage, POSIX_MESSAGE_QUEUE_BUFFER_MAX_NAME_SIZE); SolidSyslogFormatter_BoundedString(name, queueNamePrefix, sizeof(queueNamePrefix) - 1U); - SolidSyslogPosixProcessId_Get(name); + SolidSyslogFormatter_Uint32(name, (uint32_t) getpid()); SolidSyslogFormatter_AsciiCharacter(name, '_'); SolidSyslogFormatter_Uint32(name, (uint32_t) slotIndex); diff --git a/Platform/Posix/Source/SolidSyslogPosixProcessId.c b/Platform/Posix/Source/SolidSyslogPosixProcessId.c index 6d9a1803..0e437309 100644 --- a/Platform/Posix/Source/SolidSyslogPosixProcessId.c +++ b/Platform/Posix/Source/SolidSyslogPosixProcessId.c @@ -3,11 +3,12 @@ #include #include -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; -void SolidSyslogPosixProcessId_Get(struct SolidSyslogFormatter* formatter) +void SolidSyslogPosixProcessId_Get(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_Uint32(formatter, (uint32_t) getpid()); + (void) context; + SolidSyslogHeaderField_Uint32(field, (uint32_t) getpid()); } diff --git a/Platform/Windows/Interface/SolidSyslogWindowsHostname.h b/Platform/Windows/Interface/SolidSyslogWindowsHostname.h index 8174ed8f..13ad44cd 100644 --- a/Platform/Windows/Interface/SolidSyslogWindowsHostname.h +++ b/Platform/Windows/Interface/SolidSyslogWindowsHostname.h @@ -5,7 +5,7 @@ EXTERN_C_BEGIN - void SolidSyslogWindowsHostname_Get(struct SolidSyslogFormatter * formatter); + void SolidSyslogWindowsHostname_Get(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Platform/Windows/Interface/SolidSyslogWindowsProcessId.h b/Platform/Windows/Interface/SolidSyslogWindowsProcessId.h index 81bee13b..58b5f444 100644 --- a/Platform/Windows/Interface/SolidSyslogWindowsProcessId.h +++ b/Platform/Windows/Interface/SolidSyslogWindowsProcessId.h @@ -5,7 +5,7 @@ EXTERN_C_BEGIN - void SolidSyslogWindowsProcessId_Get(struct SolidSyslogFormatter * formatter); + void SolidSyslogWindowsProcessId_Get(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Platform/Windows/Source/SolidSyslogWindowsHostname.c b/Platform/Windows/Source/SolidSyslogWindowsHostname.c index ac4af1c4..e933b122 100644 --- a/Platform/Windows/Source/SolidSyslogWindowsHostname.c +++ b/Platform/Windows/Source/SolidSyslogWindowsHostname.c @@ -1,5 +1,5 @@ #include "SolidSyslogWindowsHostname.h" -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include "SolidSyslogWindowsHostnameInternal.h" /* File-local forwarder. Taking the address of an imported Windows API @@ -20,14 +20,16 @@ enum MAX_HOSTNAME_SIZE = 256U }; -void SolidSyslogWindowsHostname_Get(struct SolidSyslogFormatter* formatter) +void SolidSyslogWindowsHostname_Get(struct SolidSyslogHeaderField* field, void* context) { char hostname[MAX_HOSTNAME_SIZE]; DWORD size = sizeof(hostname); + (void) context; + if (WindowsHostname_GetComputerNameExA(ComputerNamePhysicalDnsHostname, hostname, &size) != FALSE) { hostname[sizeof(hostname) - 1U] = '\0'; - SolidSyslogFormatter_PrintUsAsciiString(formatter, hostname, sizeof(hostname)); + SolidSyslogHeaderField_PrintUsAscii(field, hostname, sizeof(hostname)); } } diff --git a/Platform/Windows/Source/SolidSyslogWindowsProcessId.c b/Platform/Windows/Source/SolidSyslogWindowsProcessId.c index d9a5d8f7..8e29d37b 100644 --- a/Platform/Windows/Source/SolidSyslogWindowsProcessId.c +++ b/Platform/Windows/Source/SolidSyslogWindowsProcessId.c @@ -1,5 +1,5 @@ #include "SolidSyslogWindowsProcessId.h" -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" #include "SolidSyslogWindowsProcessIdInternal.h" #include @@ -17,7 +17,8 @@ static DWORD WINAPI WindowsProcessId_CallGetCurrentProcessId(void) return GetCurrentProcessId(); } -void SolidSyslogWindowsProcessId_Get(struct SolidSyslogFormatter* formatter) +void SolidSyslogWindowsProcessId_Get(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_Uint32(formatter, (uint32_t) WindowsProcessId_GetCurrentProcessId()); + (void) context; + SolidSyslogHeaderField_Uint32(field, (uint32_t) WindowsProcessId_GetCurrentProcessId()); } diff --git a/Tests/Bdd/Targets/BddTargetAppNameTest.cpp b/Tests/Bdd/Targets/BddTargetAppNameTest.cpp index 94ad9a00..6c7aeebf 100644 --- a/Tests/Bdd/Targets/BddTargetAppNameTest.cpp +++ b/Tests/Bdd/Targets/BddTargetAppNameTest.cpp @@ -1,5 +1,6 @@ #include "BddTargetAppName.h" #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderFieldPrivate.h" #include "CppUTest/TestHarness.h" enum @@ -12,10 +13,12 @@ TEST_GROUP(BddTargetAppName) { SolidSyslogFormatterStorage storage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(FORMATTER_BUFFER_SIZE)]; struct SolidSyslogFormatter* formatter = nullptr; + struct SolidSyslogHeaderField field{}; void setup() override { formatter = SolidSyslogFormatter_Create(storage, FORMATTER_BUFFER_SIZE); + SolidSyslogHeaderField_FromFormatter(&field, formatter, FORMATTER_BUFFER_SIZE); } [[nodiscard]] const char* formatted() const @@ -29,55 +32,55 @@ TEST_GROUP(BddTargetAppName) TEST(BddTargetAppName, BackslashSeparatorExtractsBaseName) { BddTargetAppName_Set("dir\\binary"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("binary", formatted()); } TEST(BddTargetAppName, ForwardSlashSeparatorExtractsBaseName) { BddTargetAppName_Set("/usr/local/bin/example"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("example", formatted()); } TEST(BddTargetAppName, NoSeparatorReturnsWholeArgument) { BddTargetAppName_Set("example"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("example", formatted()); } TEST(BddTargetAppName, MixedSeparatorsUseRightmost) { BddTargetAppName_Set("C:\\msys64\\home/user/example"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("example", formatted()); } TEST(BddTargetAppName, ExeExtensionIsStripped) { BddTargetAppName_Set("app.exe"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("app", formatted()); } TEST(BddTargetAppName, UpperCaseExeExtensionIsStripped) { BddTargetAppName_Set("APP.EXE"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("APP", formatted()); } TEST(BddTargetAppName, ExeWithPathSeparatorIsStripped) { BddTargetAppName_Set("C:\\bin\\SolidSyslogBddTarget.exe"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("SolidSyslogBddTarget", formatted()); } TEST(BddTargetAppName, NonExeExtensionIsKept) { BddTargetAppName_Set("data.txt"); - BddTargetAppName_Get(formatter); + BddTargetAppName_Get(&field, nullptr); STRCMP_EQUAL("data.txt", formatted()); } diff --git a/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp b/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp index 1a1ebf2d..dc645efa 100644 --- a/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp +++ b/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp @@ -76,7 +76,8 @@ TEST_GROUP(BddTargetServiceThread) buffer = SolidSyslogPosixMessageQueueBuffer_Create(SOLIDSYSLOG_MAX_MESSAGE_SIZE, 10); store = SolidSyslogNullStore_Get(); - SolidSyslogConfig config = {buffer, sender, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; + SolidSyslogConfig config = + {buffer, sender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; solidSyslog = SolidSyslog_Create(&config); } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index a7c01ceb..5f38639a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -51,6 +51,7 @@ set(TEST_SOURCES SolidSyslogNullSdTest.cpp SolidSyslogSdValueTest.cpp SolidSyslogSdElementTest.cpp + SolidSyslogHeaderFieldTest.cpp SolidSyslogNullSenderTest.cpp SolidSyslogNullStoreTest.cpp SolidSyslogNullSecurityPolicyTest.cpp diff --git a/Tests/SolidSyslogHeaderFieldTest.cpp b/Tests/SolidSyslogHeaderFieldTest.cpp new file mode 100644 index 00000000..cc69d73f --- /dev/null +++ b/Tests/SolidSyslogHeaderFieldTest.cpp @@ -0,0 +1,120 @@ +#include + +#include "CppUTest/TestHarness.h" +#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" +#include "SolidSyslogHeaderFieldPrivate.h" + +enum +{ + TEST_BUFFER_SIZE = 64 +}; + +#define CHECK_FIELD(expected) STRCMP_EQUAL(expected, SolidSyslogFormatter_AsFormattedBuffer(formatter)) + +// clang-format off +TEST_GROUP(SolidSyslogHeaderField) +{ + SolidSyslogFormatterStorage storage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(TEST_BUFFER_SIZE)]; + struct SolidSyslogFormatter* formatter = nullptr; + struct SolidSyslogHeaderField field{}; + + void setup() override + { + formatter = SolidSyslogFormatter_Create(storage, TEST_BUFFER_SIZE); + } + + void fromFormatter(size_t maxLength) + { + SolidSyslogHeaderField_FromFormatter(&field, formatter, maxLength); + } +}; + +// clang-format on + +TEST(SolidSyslogHeaderField, PrintUsAsciiOfEmptyStringWritesNothing) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "", 32); + CHECK_FIELD(""); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiAppendsPrintableAscii) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "host01", 32); + CHECK_FIELD("host01"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiSubstitutesControlCharacter) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "a\tb", 32); + CHECK_FIELD("a?b"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiSubstitutesSpace) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "a b", 32); + CHECK_FIELD("a?b"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiStopsAtNul) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "ab\0cd", 5); + CHECK_FIELD("ab"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiTruncatesAtMaxLength) +{ + fromFormatter(32); + SolidSyslogHeaderField_PrintUsAscii(&field, "abcdef", 3); + CHECK_FIELD("abc"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiTruncatesAtFieldWidth) +{ + fromFormatter(3); + SolidSyslogHeaderField_PrintUsAscii(&field, "abcdef", 32); + CHECK_FIELD("abc"); +} + +TEST(SolidSyslogHeaderField, PrintUsAsciiBudgetIsSharedAcrossCalls) +{ + fromFormatter(4); + SolidSyslogHeaderField_PrintUsAscii(&field, "ab", 32); + SolidSyslogHeaderField_PrintUsAscii(&field, "cdef", 32); + CHECK_FIELD("abcd"); +} + +TEST(SolidSyslogHeaderField, Uint32AppendsDigits) +{ + fromFormatter(32); + SolidSyslogHeaderField_Uint32(&field, 1234); + CHECK_FIELD("1234"); +} + +TEST(SolidSyslogHeaderField, Uint32OfZeroAppendsZero) +{ + fromFormatter(32); + SolidSyslogHeaderField_Uint32(&field, 0); + CHECK_FIELD("0"); +} + +TEST(SolidSyslogHeaderField, Uint32IsDroppedOnceFieldWidthIsExhausted) +{ + fromFormatter(2); + SolidSyslogHeaderField_PrintUsAscii(&field, "ab", 32); + SolidSyslogHeaderField_Uint32(&field, 9); + CHECK_FIELD("ab"); +} + +TEST(SolidSyslogHeaderField, Uint32ExhaustsBudgetSoLaterAppendsAreDropped) +{ + fromFormatter(3); + SolidSyslogHeaderField_Uint32(&field, 123); + SolidSyslogHeaderField_PrintUsAscii(&field, "x", 32); + CHECK_FIELD("123"); +} diff --git a/Tests/SolidSyslogMessageFormatterTest.cpp b/Tests/SolidSyslogMessageFormatterTest.cpp index af010cec..b40411fd 100644 --- a/Tests/SolidSyslogMessageFormatterTest.cpp +++ b/Tests/SolidSyslogMessageFormatterTest.cpp @@ -43,9 +43,12 @@ TEST_GROUP(SolidSyslogMessageFormatter) context = { MessageFormatterTestClock, StringFake_GetHostname, + nullptr, StringFake_GetAppName, + nullptr, StringFake_GetProcessId, nullptr, + nullptr, 0 }; message = {SOLIDSYSLOG_FACILITY_LOCAL0, SOLIDSYSLOG_SEVERITY_INFORMATIONAL, nullptr, nullptr}; diff --git a/Tests/SolidSyslogPoolTest.cpp b/Tests/SolidSyslogPoolTest.cpp index 95b023f7..5135520a 100644 --- a/Tests/SolidSyslogPoolTest.cpp +++ b/Tests/SolidSyslogPoolTest.cpp @@ -40,7 +40,7 @@ TEST_GROUP(SolidSyslogPool) { fakeSender = SenderFake_Create(); buffer = SolidSyslogPassthroughBuffer_Create(fakeSender); - config = {buffer, fakeSender, nullptr, nullptr, nullptr, nullptr, + config = {buffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, SolidSyslogNullStore_Get(), nullptr, 0}; } diff --git a/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp b/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp index 26c3109d..0b49000f 100644 --- a/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp +++ b/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp @@ -188,7 +188,8 @@ TEST(SolidSyslogPosixMessageQueueBuffer, ServiceSendsMessageWrittenViaLog) { struct SolidSyslogSender* fakeSender = SenderFake_Create(); SolidSyslogStore* nullStore = SolidSyslogNullStore_Get(); - SolidSyslogConfig config = {buffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; + SolidSyslogConfig config = + {buffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; struct SolidSyslog* solidSyslog = SolidSyslog_Create(&config); SolidSyslogMessage message = {SOLIDSYSLOG_FACILITY_LOCAL0, SOLIDSYSLOG_SEVERITY_INFORMATIONAL, nullptr, nullptr}; diff --git a/Tests/SolidSyslogTest.cpp b/Tests/SolidSyslogTest.cpp index b20533ed..499163c1 100644 --- a/Tests/SolidSyslogTest.cpp +++ b/Tests/SolidSyslogTest.cpp @@ -112,7 +112,7 @@ TEST_GROUP(SolidSyslog) metaSdCounter = nullptr; metaSd = nullptr; timeQualitySd = nullptr; - config = {buffer, nullptr, nullptr, StringFake_GetHostname, StringFake_GetAppName, StringFake_GetProcessId, store, nullptr, 0}; + config = {buffer, nullptr, nullptr, StringFake_GetHostname, nullptr, StringFake_GetAppName, nullptr, StringFake_GetProcessId, nullptr, store, nullptr, 0}; solidSyslog = SolidSyslog_Create(&config); message = {SOLIDSYSLOG_FACILITY_LOCAL0, SOLIDSYSLOG_SEVERITY_INFORMATIONAL, nullptr, nullptr}; } @@ -478,7 +478,8 @@ TEST(SolidSyslogTimestamp, TimestampAppearsInCorrectMessageFieldPosition) TEST(SolidSyslog, ServiceSendsMessageReadFromBuffer) { SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); - SolidSyslogConfig serviceConfig = {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; + SolidSyslogConfig serviceConfig = + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -500,7 +501,7 @@ TEST(SolidSyslog, ServiceSendsBufferedMessageWithNullStore) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* nullStore = SolidSyslogNullStore_Get(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -522,7 +523,7 @@ TEST(SolidSyslog, ServiceSendsFromStoreWhenHasUnsent) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -545,7 +546,7 @@ TEST(SolidSyslog, ServiceMarksSentAfterSuccessfulSend) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -566,7 +567,7 @@ TEST(SolidSyslog, ServiceDoesNotMarkSentOnSendFailure) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -588,7 +589,7 @@ TEST(SolidSyslog, ServiceWritesBufferMessageToStore) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -614,7 +615,7 @@ TEST(SolidSyslog, ServiceSendsStoreMessageNotBufferMessage) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -641,7 +642,7 @@ TEST(SolidSyslog, ServiceDoesNotBypassToSenderWhenNonTransientStoreRejectsWrite) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -664,7 +665,7 @@ TEST(SolidSyslog, ServiceDoesNotSendWhenStoreReadFails) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -687,7 +688,7 @@ TEST(SolidSyslog, ServiceDoesNotMarkSentWhenSendingFromBuffer) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -780,7 +781,7 @@ TEST(SolidSyslog, ServiceDoesNothingWhenStoreIsHalted) SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); SolidSyslogStore* fakeStore = StoreFake_Create(); SolidSyslogConfig serviceConfig = - {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; + {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fakeStore, nullptr, 0}; SolidSyslog_Destroy(solidSyslog); solidSyslog = SolidSyslog_Create(&serviceConfig); @@ -801,7 +802,8 @@ TEST(SolidSyslog, ServiceDoesNothingWhenStoreIsHalted) TEST(SolidSyslog, LogAfterDestroyAndRecreateWithNullFunctionsProducesNilvalues) { SolidSyslog_Destroy(solidSyslog); - SolidSyslogConfig nilConfig = {buffer, nullptr, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; + SolidSyslogConfig nilConfig = + {buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; solidSyslog = SolidSyslog_Create(&nilConfig); Log(); CHECK_TIMESTAMP_IS_NILVALUE(); @@ -840,7 +842,7 @@ TEST_GROUP(SolidSyslogLifecycle) [[nodiscard]] SolidSyslogConfig validConfig() const { - return {buffer, sender, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; + return {buffer, sender, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; } }; diff --git a/Tests/SolidSyslogWindowsHostnameTest.cpp b/Tests/SolidSyslogWindowsHostnameTest.cpp index 3e38c86d..6677b745 100644 --- a/Tests/SolidSyslogWindowsHostnameTest.cpp +++ b/Tests/SolidSyslogWindowsHostnameTest.cpp @@ -1,5 +1,6 @@ #include "CppUTest/TestHarness.h" #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderFieldPrivate.h" #include "SolidSyslogWindowsHostname.h" #include "SolidSyslogWindowsHostnameInternal.h" @@ -38,6 +39,7 @@ TEST_GROUP(SolidSyslogWindowsHostname) { SolidSyslogFormatterStorage storage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(FORMATTER_BUFFER_SIZE)]; struct SolidSyslogFormatter* formatter = nullptr; + struct SolidSyslogHeaderField field{}; void setup() override { @@ -45,6 +47,7 @@ TEST_GROUP(SolidSyslogWindowsHostname) fakeReturnValue = TRUE; UT_PTR_SET(WindowsHostname_GetComputerNameExA, FakeGetComputerNameExA); formatter = SolidSyslogFormatter_Create(storage, FORMATTER_BUFFER_SIZE); + SolidSyslogHeaderField_FromFormatter(&field, formatter, FORMATTER_BUFFER_SIZE); } const char* formatted() const @@ -57,21 +60,21 @@ TEST_GROUP(SolidSyslogWindowsHostname) TEST(SolidSyslogWindowsHostname, WritesFakeHostnameIntoFormatter) { - SolidSyslogWindowsHostname_Get(formatter); + SolidSyslogWindowsHostname_Get(&field, nullptr); STRCMP_EQUAL("winhost", formatted()); } TEST(SolidSyslogWindowsHostname, WritesNothingWhenApiFails) { fakeReturnValue = FALSE; - SolidSyslogWindowsHostname_Get(formatter); + SolidSyslogWindowsHostname_Get(&field, nullptr); STRCMP_EQUAL("", formatted()); } TEST(SolidSyslogWindowsHostname, EmptyHostnameProducesEmptyString) { fakeHostname = ""; - SolidSyslogWindowsHostname_Get(formatter); + SolidSyslogWindowsHostname_Get(&field, nullptr); STRCMP_EQUAL("", formatted()); } @@ -83,6 +86,6 @@ TEST(SolidSyslogWindowsHostname, HostnameTooLongForBufferProducesEmptyString) memset(longName, 'x', 260); longName[260] = '\0'; fakeHostname = longName; - SolidSyslogWindowsHostname_Get(formatter); + SolidSyslogWindowsHostname_Get(&field, nullptr); STRCMP_EQUAL("", formatted()); } diff --git a/Tests/SolidSyslogWindowsProcessIdTest.cpp b/Tests/SolidSyslogWindowsProcessIdTest.cpp index 8f9b4787..7327de45 100644 --- a/Tests/SolidSyslogWindowsProcessIdTest.cpp +++ b/Tests/SolidSyslogWindowsProcessIdTest.cpp @@ -1,5 +1,6 @@ #include "CppUTest/TestHarness.h" #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderFieldPrivate.h" #include "SolidSyslogWindowsProcessId.h" #include "SolidSyslogWindowsProcessIdInternal.h" @@ -22,12 +23,14 @@ TEST_GROUP(SolidSyslogWindowsProcessId) { SolidSyslogFormatterStorage storage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(FORMATTER_BUFFER_SIZE)]; struct SolidSyslogFormatter* formatter = nullptr; + struct SolidSyslogHeaderField field{}; void setup() override { fakePid = 4321; UT_PTR_SET(WindowsProcessId_GetCurrentProcessId, FakeGetCurrentProcessId); formatter = SolidSyslogFormatter_Create(storage, FORMATTER_BUFFER_SIZE); + SolidSyslogHeaderField_FromFormatter(&field, formatter, FORMATTER_BUFFER_SIZE); } const char* formatted() const @@ -40,20 +43,20 @@ TEST_GROUP(SolidSyslogWindowsProcessId) TEST(SolidSyslogWindowsProcessId, WritesFakePidAsDecimal) { - SolidSyslogWindowsProcessId_Get(formatter); + SolidSyslogWindowsProcessId_Get(&field, nullptr); STRCMP_EQUAL("4321", formatted()); } TEST(SolidSyslogWindowsProcessId, WritesZeroWhenPidIsZero) { fakePid = 0; - SolidSyslogWindowsProcessId_Get(formatter); + SolidSyslogWindowsProcessId_Get(&field, nullptr); STRCMP_EQUAL("0", formatted()); } TEST(SolidSyslogWindowsProcessId, WritesMaxDwordValueAsDecimal) { fakePid = 0xFFFFFFFFU; - SolidSyslogWindowsProcessId_Get(formatter); + SolidSyslogWindowsProcessId_Get(&field, nullptr); STRCMP_EQUAL("4294967295", formatted()); } diff --git a/Tests/StringFake.c b/Tests/StringFake.c index 85fd6bb1..a6a89236 100644 --- a/Tests/StringFake.c +++ b/Tests/StringFake.c @@ -2,9 +2,9 @@ #include -#include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderField.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; static const char* fakeHostname; static const char* fakeAppName; @@ -22,9 +22,10 @@ void StringFake_SetHostname(const char* hostname) fakeHostname = hostname; } -void StringFake_GetHostname(struct SolidSyslogFormatter* formatter) +void StringFake_GetHostname(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_PrintUsAsciiString(formatter, fakeHostname, strlen(fakeHostname)); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, fakeHostname, strlen(fakeHostname)); } void StringFake_SetAppName(const char* appName) @@ -32,9 +33,10 @@ void StringFake_SetAppName(const char* appName) fakeAppName = appName; } -void StringFake_GetAppName(struct SolidSyslogFormatter* formatter) +void StringFake_GetAppName(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_PrintUsAsciiString(formatter, fakeAppName, strlen(fakeAppName)); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, fakeAppName, strlen(fakeAppName)); } void StringFake_SetProcessId(const char* procId) @@ -42,7 +44,8 @@ void StringFake_SetProcessId(const char* procId) fakeProcessId = procId; } -void StringFake_GetProcessId(struct SolidSyslogFormatter* formatter) +void StringFake_GetProcessId(struct SolidSyslogHeaderField* field, void* context) { - SolidSyslogFormatter_PrintUsAsciiString(formatter, fakeProcessId, strlen(fakeProcessId)); + (void) context; + SolidSyslogHeaderField_PrintUsAscii(field, fakeProcessId, strlen(fakeProcessId)); } diff --git a/Tests/StringFake.h b/Tests/StringFake.h index 90b8b2e5..5b862483 100644 --- a/Tests/StringFake.h +++ b/Tests/StringFake.h @@ -3,17 +3,17 @@ #include "ExternC.h" -struct SolidSyslogFormatter; +struct SolidSyslogHeaderField; EXTERN_C_BEGIN void StringFake_Reset(void); void StringFake_SetHostname(const char* hostname); - void StringFake_GetHostname(struct SolidSyslogFormatter * formatter); + void StringFake_GetHostname(struct SolidSyslogHeaderField * field, void* context); void StringFake_SetAppName(const char* appName); - void StringFake_GetAppName(struct SolidSyslogFormatter * formatter); + void StringFake_GetAppName(struct SolidSyslogHeaderField * field, void* context); void StringFake_SetProcessId(const char* procId); - void StringFake_GetProcessId(struct SolidSyslogFormatter * formatter); + void StringFake_GetProcessId(struct SolidSyslogHeaderField * field, void* context); EXTERN_C_END diff --git a/Tests/StringFakeTest.cpp b/Tests/StringFakeTest.cpp index 2dd61d2f..386070ac 100644 --- a/Tests/StringFakeTest.cpp +++ b/Tests/StringFakeTest.cpp @@ -1,4 +1,5 @@ #include "SolidSyslogFormatter.h" +#include "SolidSyslogHeaderFieldPrivate.h" #include "StringFake.h" #include "CppUTest/TestHarness.h" @@ -14,10 +15,12 @@ TEST_GROUP(StringFake) { SolidSyslogFormatterStorage storage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(TEST_BUFFER_SIZE)]; SolidSyslogFormatter* formatter; + struct SolidSyslogHeaderField field{}; void setup() override { formatter = SolidSyslogFormatter_Create(storage, TEST_BUFFER_SIZE); + SolidSyslogHeaderField_FromFormatter(&field, formatter, TEST_BUFFER_SIZE); StringFake_Reset(); } }; @@ -26,7 +29,7 @@ TEST_GROUP(StringFake) TEST(StringFake, ReturnsEmptyStringAfterReset) { - StringFake_GetHostname(formatter); + StringFake_GetHostname(&field, nullptr); STRCMP_EQUAL("", SolidSyslogFormatter_AsFormattedBuffer(formatter)); LONGS_EQUAL(0, SolidSyslogFormatter_Length(formatter)); } @@ -34,7 +37,7 @@ TEST(StringFake, ReturnsEmptyStringAfterReset) TEST(StringFake, ReturnsConfiguredHostname) { StringFake_SetHostname("MyHost"); - StringFake_GetHostname(formatter); + StringFake_GetHostname(&field, nullptr); STRCMP_EQUAL("MyHost", SolidSyslogFormatter_AsFormattedBuffer(formatter)); LONGS_EQUAL(6, SolidSyslogFormatter_Length(formatter)); } diff --git a/docs/rfc-compliance.md b/docs/rfc-compliance.md index 00eb2d94..c9c0ea78 100644 --- a/docs/rfc-compliance.md +++ b/docs/rfc-compliance.md @@ -23,10 +23,10 @@ Status key: | 6.2.5 | PROCID — max 128 chars, PRINTUSASCII | Supported | Truncated to 128. Non-PRINTUSASCII bytes substituted with `?` | | 6.2.6 | MSGID — max 32 chars, PRINTUSASCII | Supported | Truncated to 32. Non-PRINTUSASCII bytes substituted with `?` | | 6.3 | STRUCTURED-DATA — SD-ELEMENTs or NILVALUE | Supported | Extensible via `SolidSyslogStructuredData` vtable | -| 6.3.3 | SD-PARAM value escaping (`]`, `\`, `"`) | Supported | `SolidSyslogFormatter_EscapedString` — RFC 3629 UTF-8 validated, ill-formed input substituted per-byte with U+FFFD (Unicode §3.9); `OriginSd` escapes software, swVersion, enterpriseId, and each ip via this primitive; `MetaSd` escapes language via the integrator's `SolidSyslogStringFunction` callback that wraps the same primitive. SD-NAME / SD-ID syntax validation only matters once callers can supply names — landed under [E14](https://github.com/DavidCozens/solid-syslog/issues/64) (Custom Structured Data); the standard SDs (meta / timeQuality / origin) use compile-time-constant names | +| 6.3.3 | SD-PARAM value escaping (`]`, `\`, `"`) | Supported | `SolidSyslogFormatter_EscapedString` — RFC 3629 UTF-8 validated, ill-formed input substituted per-byte with U+FFFD (Unicode §3.9); `OriginSd` escapes software, swVersion, enterpriseId, and each ip via this primitive; `MetaSd` escapes language via the integrator's `SolidSyslogSdValueFunction` callback streaming into a `SolidSyslogSdValue`, which applies the same escaping. SD-NAME / SD-ID syntax validation only matters once callers can supply names — landed under [E14](https://github.com/DavidCozens/solid-syslog/issues/64) (Custom Structured Data); the standard SDs (meta / timeQuality / origin) use compile-time-constant names | | 6.3.3 | timeQuality SD — tzKnown, isSynced, syncAccuracy | Supported | `SolidSyslogTimeQualitySd` | | 6.3.4, 7.2 | origin SD — software, swVersion, enterpriseId, ip | Supported | `SolidSyslogOriginSd` covers all four §7.2 parameters. `software`, `swVersion`, and `enterpriseId` are static strings supplied via `SolidSyslogOriginSdConfig`; each is escaped per §6.3.3 via `SolidSyslogFormatter_EscapedString` and pre-formatted at Create time into an internal storage buffer. `ip` is repeatable per RFC 5424 §7.2 and sourced via two callbacks (`SolidSyslogOriginIpCountFunction`, `SolidSyslogOriginIpAtFunction`) so multi-homed hosts can reflect runtime address changes; the library asks for a count then loops 0..N-1 framing each `ip="…"` token (with a leading space) while the integrator's at-callback writes one escaped IP value per call (this avoids a formatter rewind primitive). All four parameters are independently optional — a NULL field or NULL callback omits the corresponding parameter from the SD-ELEMENT. Per-IP value capped at 64 chars via `_EscapedString`; no library-side cap on the IP count (bounded by `SOLIDSYSLOG_MAX_MESSAGE_SIZE`). Bare `[origin]` with no parameters is RFC-legal (§7.2 marks all params OPTIONAL, no SHOULD enforcement) and is what the library emits when the integrator wires nothing | -| 6.3.5, 7.3 | meta SD — sequenceId, sysUpTime, language | Supported | `SolidSyslogMetaSd` covers all three IANA-registered parameters. `sequenceId` (§7.3.1) sourced via an injected `SolidSyslogAtomicCounter`. `sysUpTime` (§7.3.2 / RFC 3418 `TimeTicks`) sourced via a `SolidSyslogSysUpTimeFunction` callback returning `uint32_t` hundredths; reference platform integrations are `SolidSyslogPosixSysUpTime` (`clock_gettime(CLOCK_BOOTTIME)`) and `SolidSyslogWindowsSysUpTime` (`GetTickCount64`), with the cast to `uint32_t` providing RFC 3418's natural wrap. `language` (§7.3.3 / BCP 47) sourced via a `SolidSyslogStringFunction` callback that writes through `SolidSyslogFormatter_EscapedString` to satisfy SD-PARAM-VALUE escaping per §6.3.3. All three independently optional — a NULL field in `SolidSyslogMetaSdConfig` omits that parameter from the SD-ELEMENT | +| 6.3.5, 7.3 | meta SD — sequenceId, sysUpTime, language | Supported | `SolidSyslogMetaSd` covers all three IANA-registered parameters. `sequenceId` (§7.3.1) sourced via an injected `SolidSyslogAtomicCounter`. `sysUpTime` (§7.3.2 / RFC 3418 `TimeTicks`) sourced via a `SolidSyslogSysUpTimeFunction` callback returning `uint32_t` hundredths; reference platform integrations are `SolidSyslogPosixSysUpTime` (`clock_gettime(CLOCK_BOOTTIME)`) and `SolidSyslogWindowsSysUpTime` (`GetTickCount64`), with the cast to `uint32_t` providing RFC 3418's natural wrap. `language` (§7.3.3 / BCP 47) sourced via a `SolidSyslogSdValueFunction` callback streaming into a `SolidSyslogSdValue`, which applies SD-PARAM-VALUE escaping per §6.3.3. All three independently optional — a NULL field in `SolidSyslogMetaSdConfig` omits that parameter from the SD-ELEMENT | | 6.3.5, 7.3.1 | meta SD — sequenceId wraps at 2147483647 to 1 | Supported | `SolidSyslogAtomicCounter` wraps via CAS-loop in [1, 2³¹ - 1]; never returns 0; never above max. AtomicCounter is a vtable abstraction — concrete impls are `SolidSyslogStdAtomicCounter` (C11 `` + `atomic_compare_exchange_strong_explicit`) on POSIX/clang/gcc/modern MSVC, and `SolidSyslogWindowsAtomicCounter` (`volatile LONG` + `InterlockedCompareExchange`) on legacy MSVC. The integrator picks one at setup time by calling the relevant platform's `_Create`; CMake's `HAVE_STDATOMIC_H` / `HAVE_WINDOWS_INTERLOCKED` checks gate which platform sources are compiled. sequenceId is assigned at the point of message raise (application-layer originator), preserving end-to-end loss-detection across the internal buffer / store-and-forward / transport pipeline. Trade-off: under concurrent raise from multiple threads, a small reorder window may occur in transmitted IDs (adjacent IDs may invert, since buffer/transport scheduling between raise and wire is not under library control). All IDs remain unique and non-zero — SIEMs performing gap detection identify message loss correctly; SIEMs requiring strict monotonic ordering should sort by timestamp | | 6.4 | MSG — UTF-8 preferred | Supported | RFC 3629 UTF-8 validated at the formatter primitives (`SolidSyslogFormatter_BoundedString`), with ill-formed input substituted per-byte with U+FFFD (Unicode §3.9). MSG is prefixed with the §6.4 UTF-8 BOM (`%xEF.BB.BF`) unconditionally; if the caller's body already begins with a BOM it is stripped so the wire frame contains exactly one ([S12.13](https://github.com/DavidCozens/solid-syslog/issues/219)). Truncation preserves codepoint boundaries at both layers: the formatter clips at `SOLIDSYSLOG_MAX_MESSAGE_SIZE` without splitting a codepoint ([S12.10](https://github.com/DavidCozens/solid-syslog/issues/121)), and on UDP the sender walks back over any partial codepoint when the kernel reports `EMSGSIZE` for the path MTU ([S12.12](https://github.com/DavidCozens/solid-syslog/issues/210)). TCP/TLS streams fragment transparently at the transport layer and so do not need a path-MTU trim | | 8.1 | Message size — max 2048 recommended | Supported | Default `SOLIDSYSLOG_MAX_MESSAGE_SIZE` = 2048, matching the §8.1 SHOULD value. Per-target override via a CMake variable is planned in [E21 #217](https://github.com/DavidCozens/solid-syslog/issues/217) for memory-constrained MCUs | diff --git a/misra_suppressions.txt b/misra_suppressions.txt index 3a942a3a..c8b486a6 100644 --- a/misra_suppressions.txt +++ b/misra_suppressions.txt @@ -103,19 +103,19 @@ misra-c2012-18.7:Core/Source/SolidSyslogFormatter.c:12 # D.006 — Rule 11.8: const-strip false-positives + platform-API (Winsock, lwIP) # See docs/misra-deviations.md#d006 misra-c2012-11.8:Core/Source/BlockSequence.c:465 -misra-c2012-11.8:Core/Source/SolidSyslog.c:53 -misra-c2012-11.8:Core/Source/SolidSyslog.c:54 -misra-c2012-11.8:Core/Source/SolidSyslog.c:55 -misra-c2012-11.8:Core/Source/SolidSyslog.c:56 -misra-c2012-11.8:Core/Source/SolidSyslog.c:57 -misra-c2012-11.8:Core/Source/SolidSyslog.c:58 -misra-c2012-11.8:Core/Source/SolidSyslog.c:59 -misra-c2012-11.8:Core/Source/SolidSyslog.c:60 -misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:58 +misra-c2012-11.8:Core/Source/SolidSyslog.c:65 +misra-c2012-11.8:Core/Source/SolidSyslog.c:66 +misra-c2012-11.8:Core/Source/SolidSyslog.c:67 +misra-c2012-11.8:Core/Source/SolidSyslog.c:68 +misra-c2012-11.8:Core/Source/SolidSyslog.c:69 +misra-c2012-11.8:Core/Source/SolidSyslog.c:70 +misra-c2012-11.8:Core/Source/SolidSyslog.c:71 +misra-c2012-11.8:Core/Source/SolidSyslog.c:72 misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:60 misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:62 -misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:64 -misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:68 +misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:69 +misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:76 +misra-c2012-11.8:Core/Source/SolidSyslogMessageFormatter.c:85 misra-c2012-11.8:Core/Source/SolidSyslogBlockStoreStatic.c:46 misra-c2012-11.8:Core/Source/SolidSyslogBlockStoreStatic.c:117 misra-c2012-11.8:Core/Source/SolidSyslogBlockStoreStatic.c:143 @@ -171,7 +171,7 @@ misra-c2012-5.7:Core/Interface/SolidSyslogFormatter.h:14 misra-c2012-5.7:Core/Interface/SolidSyslogSecurityPolicyDefinition.h:13 misra-c2012-5.7:Core/Interface/SolidSyslogTimeQuality.h:12 misra-c2012-5.7:Core/Interface/SolidSyslogTransport.h:5 -misra-c2012-5.7:Core/Source/SolidSyslogMessageFormatter.c:20 +misra-c2012-5.7:Core/Source/SolidSyslogMessageFormatter.c:21 misra-c2012-5.7:Core/Source/SolidSyslogCircularBuffer.c:19 misra-c2012-5.7:Core/Source/SolidSyslogCrc16.c:12 misra-c2012-5.7:Core/Source/SolidSyslogCrc16Policy.c:10