Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogFormatter.h"
6 : : #include "SolidSyslogMacros.h"
7 : : #include "SolidSyslogUtf8.h"
8 : :
9 : : #include <stdbool.h>
10 : : #include <stddef.h>
11 : :
12 : : struct SolidSyslogFormatter
13 : : {
14 : : size_t Size;
15 : : size_t Position;
16 : : char Buffer[];
17 : : };
18 : :
19 : : SOLIDSYSLOG_STATIC_ASSERT(
20 : : sizeof(struct SolidSyslogFormatter) == (SOLIDSYSLOG_FORMATTER_OVERHEAD * sizeof(SolidSyslogFormatterStorage)),
21 : : "SOLIDSYSLOG_FORMATTER_OVERHEAD does not match struct layout"
22 : : );
23 : :
24 : : static const char NON_PRINTABLE_SUBSTITUTE = '?';
25 : :
26 : : /* UTF-8 replacement character U+FFFD, emitted in place of each invalid
27 : : * byte per Unicode section 3.9 best practice for per-byte maximal subpart. */
28 : : static const char REPLACEMENT_CHARACTER[] = {'\xEF', '\xBF', '\xBD'};
29 : :
30 : : /* Mutable state threaded through the EscapedString writer helpers.
31 : : * sourcePos walks the source until NUL; decodedLength counts bytes the
32 : : * reader's decoder would extract; exhausted flips when a writer can't
33 : : * fit and the loop must terminate. */
34 : : struct EscapedContext
35 : : {
36 : : struct SolidSyslogFormatter* Formatter;
37 : : const char* Source;
38 : : size_t SourcePos;
39 : : size_t DecodedLength;
40 : : size_t MaxDecodedLength;
41 : : bool Exhausted;
42 : : };
43 : :
44 : : static inline bool Formatter_CodepointFits(size_t codepointLength, size_t remainingDecodedLength);
45 : : static inline bool Formatter_Fits(const struct EscapedContext* context, size_t decodedAdvance);
46 : : static inline bool Formatter_HasCapacity(const struct SolidSyslogFormatter* formatter);
47 : : static inline bool Formatter_IsAboveUnicodeMaxEncoding(char lead, char continuation1);
48 : : static inline bool Formatter_IsOverlongFourByteEncoding(char lead, char continuation1);
49 : : static inline bool Formatter_IsOverlongThreeByteEncoding(char lead, char continuation1);
50 : : static inline bool Formatter_IsOverlongTwoByteLead(char byte);
51 : : static inline bool Formatter_IsAsciiCharacter(char value);
52 : : static inline bool Formatter_IsExhausted(const struct EscapedContext* context);
53 : : static inline bool Formatter_IsPrintableUsAscii(char value);
54 : : static inline bool Formatter_IsUtf16SurrogateEncoding(char lead, char continuation1);
55 : : static inline bool Formatter_IsValidUtf8FourByte(char lead, char continuation1, char continuation2, char continuation3);
56 : : static inline bool Formatter_IsValidUtf8ThreeByte(char lead, char continuation1, char continuation2);
57 : : static inline bool Formatter_IsValidUtf8TwoByte(char lead, char continuation1);
58 : : static inline bool Formatter_NeedsEscape(char value);
59 : : static inline char Formatter_DigitToChar(uint32_t value);
60 : : static size_t Formatter_CountDigits(uint32_t value);
61 : : static inline size_t Formatter_Utf8CodepointLength(const char* source);
62 : : static inline void Formatter_Exhaust(struct EscapedContext* context);
63 : : static inline void Formatter_NullTerminate(struct SolidSyslogFormatter* formatter);
64 : : static inline void Formatter_TrimTruncatedMultiByteTail(struct SolidSyslogFormatter* formatter);
65 : : static inline void Formatter_WriteBytes(struct SolidSyslogFormatter* formatter, const char* bytes, size_t count);
66 : : static inline void Formatter_WriteChar(struct SolidSyslogFormatter* formatter, char value);
67 : : static inline void Formatter_WriteCodepoint(struct EscapedContext* context);
68 : : /* NOLINTBEGIN(bugprone-easily-swappable-parameters) -- 3 callers in this file pass compile-time constants or codepointLength; param names disambiguate */
69 : : static inline void Formatter_WriteContext(
70 : : struct EscapedContext* context,
71 : : const char* bytes,
72 : : size_t byteCount,
73 : : size_t sourceAdvance,
74 : : size_t decodedAdvance
75 : : );
76 : : /* NOLINTEND(bugprone-easily-swappable-parameters) */
77 : : static inline void Formatter_WriteEscaped(struct EscapedContext* context);
78 : : static inline void Formatter_WritePrintableUsAsciiChar(struct SolidSyslogFormatter* formatter, char value);
79 : : static inline void Formatter_WriteReplacement(struct EscapedContext* context);
80 : :
81 : 46441 : struct SolidSyslogFormatter* SolidSyslogFormatter_Create(SolidSyslogFormatterStorage* storage, size_t bufferSize)
82 : : {
83 : 46441 : struct SolidSyslogFormatter* formatter = (struct SolidSyslogFormatter*) storage;
84 : 46441 : formatter->Size = bufferSize;
85 : 46441 : formatter->Position = 0;
86 : 46441 : Formatter_NullTerminate(formatter);
87 : 46441 : return formatter;
88 : : }
89 : :
90 : 349746 : static inline void Formatter_NullTerminate(struct SolidSyslogFormatter* formatter)
91 : : {
92 [ + + ]: 349746 : if (formatter->Size > 0U)
93 : : {
94 : 343025 : formatter->Buffer[formatter->Position] = '\0';
95 : : }
96 : 349746 : }
97 : :
98 : 144253 : void SolidSyslogFormatter_AsciiCharacter(struct SolidSyslogFormatter* formatter, char value)
99 : : {
100 : 144253 : char emitted = value;
101 [ + + ]: 144253 : if (!Formatter_IsAsciiCharacter(emitted))
102 : : {
103 : 3 : emitted = NON_PRINTABLE_SUBSTITUTE;
104 : : }
105 : 144253 : Formatter_WriteChar(formatter, emitted);
106 : 144253 : Formatter_NullTerminate(formatter);
107 : 144253 : }
108 : :
109 : 6558 : void SolidSyslogFormatter_Bom(struct SolidSyslogFormatter* formatter)
110 : : {
111 : : /* UTF-8 byte order mark U+FEFF, required at the start of a UTF-8 MSG
112 : : * by RFC 5424 section 6.4. */
113 : : static const char UTF8_BOM[] = {'\xEF', '\xBB', '\xBF'};
114 : 6558 : Formatter_WriteBytes(formatter, UTF8_BOM, sizeof(UTF8_BOM));
115 : 6558 : Formatter_NullTerminate(formatter);
116 : 6558 : }
117 : :
118 : 26660 : void SolidSyslogFormatter_NilValue(struct SolidSyslogFormatter* formatter)
119 : : {
120 : : /* RFC 5424 section 6 NILVALUE - a single '-' standing in for an absent field. */
121 : 26660 : SolidSyslogFormatter_AsciiCharacter(formatter, '-');
122 : 26660 : }
123 : :
124 : 144253 : static inline bool Formatter_IsAsciiCharacter(char value)
125 : : {
126 [ + + + + ]: 144253 : return (value == ' ') || Formatter_IsPrintableUsAscii(value);
127 : : }
128 : :
129 : 974867 : static inline void Formatter_WriteChar(struct SolidSyslogFormatter* formatter, char value)
130 : : {
131 [ + + ]: 974867 : if (Formatter_HasCapacity(formatter))
132 : : {
133 : 974558 : formatter->Buffer[formatter->Position] = value;
134 : 974558 : formatter->Position++;
135 : : }
136 : 974867 : }
137 : :
138 : 974867 : static inline bool Formatter_HasCapacity(const struct SolidSyslogFormatter* formatter)
139 : : {
140 [ + + + + ]: 974867 : return (formatter->Size > 0U) && (formatter->Position < (formatter->Size - 1U));
141 : : }
142 : :
143 : : /*
144 : : * UTF-8 validation per RFC 3629 section 4 and Unicode section 3.9.
145 : : *
146 : : * 1-byte 0xxxxxxx U+0000 - U+007F
147 : : * 2-byte 110xxxxx 10xxxxxx U+0080 - U+07FF lead C2-DF
148 : : * 3-byte 1110xxxx 10xxxxxx 10xxxxxx U+0800 - U+FFFF lead E0-EF
149 : : * 4-byte 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U+10000 - U+10FFFF lead F0-F4
150 : : *
151 : : * Excluded sequences:
152 : : * C0, C1 overlong 2-byte encodings
153 : : * E0 + cont1 80-9F overlong 3-byte encodings
154 : : * ED + cont1 A0-BF UTF-16 surrogate range (U+D800..U+DFFF)
155 : : * F0 + cont1 80-8F overlong 4-byte encodings
156 : : * F4 + cont1 90-BF codepoints above U+10FFFF
157 : : * F5-F7 codepoints above U+10FFFF
158 : : * F8-FF 5+ byte prefix patterns removed by RFC 3629
159 : : */
160 : :
161 : 63315 : void SolidSyslogFormatter_BoundedString(struct SolidSyslogFormatter* formatter, const char* source, size_t maxLength)
162 : : {
163 : 63315 : size_t len = 0U;
164 : :
165 [ + + + + ]: 616357 : while ((len < maxLength) && (source[len] != '\0'))
166 : : {
167 : 553042 : size_t codepointLength = Formatter_Utf8CodepointLength(&source[len]);
168 : :
169 [ + + ]: 553042 : if (Formatter_CodepointFits(codepointLength, maxLength - len))
170 : : {
171 : 552982 : Formatter_WriteBytes(formatter, &source[len], codepointLength);
172 : 552982 : len += codepointLength;
173 : : }
174 : : else
175 : : {
176 : 60 : Formatter_WriteBytes(formatter, REPLACEMENT_CHARACTER, sizeof(REPLACEMENT_CHARACTER));
177 : 60 : len += 1U;
178 : : }
179 : : }
180 : 63315 : Formatter_NullTerminate(formatter);
181 : 63315 : }
182 : :
183 : 553042 : static inline bool Formatter_CodepointFits(size_t codepointLength, size_t remainingDecodedLength)
184 : : {
185 [ + + + + ]: 553042 : return (codepointLength > 0U) && (codepointLength <= remainingDecodedLength);
186 : : }
187 : :
188 : 577695 : static inline size_t Formatter_Utf8CodepointLength(const char* source)
189 : : {
190 : 577695 : size_t length = 0U;
191 : :
192 [ + + ]: 577695 : if (SolidSyslogUtf8_IsAsciiByte(source[0]))
193 : : {
194 : 577596 : length = 1;
195 : : }
196 [ + + + + ]: 99 : else if ((source[1] != '\0') && Formatter_IsValidUtf8TwoByte(source[0], source[1]))
197 : : {
198 : 11 : length = 2;
199 : : }
200 [ + + + + : 132 : else if ((source[1] != '\0') && (source[2] != '\0') &&
+ + ]
201 : 44 : Formatter_IsValidUtf8ThreeByte(source[0], source[1], source[2]))
202 : : {
203 : 10 : length = 3;
204 : : }
205 [ + + + + : 98 : else if ((source[1] != '\0') && (source[2] != '\0') && (source[3] != '\0') &&
+ + + + ]
206 : 20 : Formatter_IsValidUtf8FourByte(source[0], source[1], source[2], source[3]))
207 : : {
208 : 11 : length = 4;
209 : : }
210 : : else
211 : : {
212 : : /* lead does not start a valid 1-/2-/3-/4-byte UTF-8 sequence - length stays 0 */
213 : : }
214 : :
215 : 577695 : return length;
216 : : }
217 : :
218 : 70 : static inline bool Formatter_IsValidUtf8TwoByte(char lead, char continuation1)
219 : : {
220 [ + + + + : 82 : return SolidSyslogUtf8_IsTwoByteLead(lead) && !Formatter_IsOverlongTwoByteLead(lead) &&
+ + ]
221 : 12 : SolidSyslogUtf8_IsContinuationByte(continuation1);
222 : : }
223 : :
224 : 15 : static inline bool Formatter_IsOverlongTwoByteLead(char byte)
225 : : {
226 : 15 : return ((unsigned char) byte & 0xFEU) == 0xC0U;
227 : : }
228 : :
229 : 44 : static inline bool Formatter_IsValidUtf8ThreeByte(char lead, char continuation1, char continuation2)
230 : : {
231 [ + + + + ]: 74 : return SolidSyslogUtf8_IsThreeByteLead(lead) && SolidSyslogUtf8_IsContinuationByte(continuation1) &&
232 : 14 : SolidSyslogUtf8_IsContinuationByte(continuation2) &&
233 [ + + + + ]: 71 : !Formatter_IsOverlongThreeByteEncoding(lead, continuation1) &&
234 [ + + ]: 11 : !Formatter_IsUtf16SurrogateEncoding(lead, continuation1);
235 : : }
236 : :
237 : 13 : static inline bool Formatter_IsOverlongThreeByteEncoding(char lead, char continuation1)
238 : : {
239 [ + + + + ]: 13 : return ((unsigned char) lead == 0xE0U) && (((unsigned char) continuation1 & 0xE0U) == 0x80U);
240 : : }
241 : :
242 : 11 : static inline bool Formatter_IsUtf16SurrogateEncoding(char lead, char continuation1)
243 : : {
244 [ + + + + ]: 11 : return ((unsigned char) lead == 0xEDU) && (((unsigned char) continuation1 & 0xE0U) == 0xA0U);
245 : : }
246 : :
247 : 20 : static inline bool Formatter_IsValidUtf8FourByte(char lead, char continuation1, char continuation2, char continuation3)
248 : : {
249 [ + + + + ]: 57 : return SolidSyslogUtf8_IsFourByteLead(lead) && SolidSyslogUtf8_IsContinuationByte(continuation1) &&
250 [ + + ]: 35 : SolidSyslogUtf8_IsContinuationByte(continuation2) && SolidSyslogUtf8_IsContinuationByte(continuation3) &&
251 [ + + + + ]: 54 : !Formatter_IsOverlongFourByteEncoding(lead, continuation1) &&
252 [ + + ]: 15 : !Formatter_IsAboveUnicodeMaxEncoding(lead, continuation1);
253 : : }
254 : :
255 : 16 : static inline bool Formatter_IsOverlongFourByteEncoding(char lead, char continuation1)
256 : : {
257 [ + + + + ]: 16 : return ((unsigned char) lead == 0xF0U) && (((unsigned char) continuation1 & 0xF0U) == 0x80U);
258 : : }
259 : :
260 : : /* NOLINTBEGIN(bugprone-easily-swappable-parameters) -- lead and continuation1 are byte values with distinct semantic roles, swap would not even compile a different result given the lead's value constraint */
261 : 15 : static inline bool Formatter_IsAboveUnicodeMaxEncoding(char lead, char continuation1)
262 : : {
263 : 15 : unsigned char uLead = (unsigned char) lead;
264 [ + + + + ]: 15 : bool f4WithCont1Above8F = (uLead == 0xF4U) && (((unsigned char) continuation1 & 0xF0U) != 0x80U);
265 [ + + + - ]: 15 : bool f5OrHigherLead = (uLead >= 0xF5U) && (uLead <= 0xF7U);
266 [ + + + + ]: 15 : return f4WithCont1Above8F || f5OrHigherLead;
267 : : }
268 : :
269 : : /* NOLINTEND(bugprone-easily-swappable-parameters) */
270 : :
271 : 584422 : static inline void Formatter_WriteBytes(struct SolidSyslogFormatter* formatter, const char* bytes, size_t count)
272 : : {
273 [ + + ]: 1182333 : for (size_t i = 0U; i < count; i++)
274 : : {
275 : 597911 : Formatter_WriteChar(formatter, bytes[i]);
276 : : }
277 : 584422 : }
278 : :
279 : 22354 : void SolidSyslogFormatter_EscapedString(
280 : : struct SolidSyslogFormatter* formatter,
281 : : const char* source,
282 : : size_t maxDecodedLength
283 : : )
284 : : {
285 : 22354 : struct EscapedContext context = {
286 : : .Formatter = formatter,
287 : : .Source = source,
288 : : .SourcePos = 0,
289 : : .DecodedLength = 0,
290 : : .MaxDecodedLength = maxDecodedLength,
291 : : .Exhausted = false,
292 : : };
293 : :
294 [ + + ]: 47185 : while (!Formatter_IsExhausted(&context))
295 : : {
296 [ + + ]: 24831 : if (Formatter_NeedsEscape(source[context.SourcePos]))
297 : : {
298 : 178 : Formatter_WriteEscaped(&context);
299 : : }
300 : : else
301 : : {
302 : 24653 : Formatter_WriteCodepoint(&context);
303 : : }
304 : : }
305 : 22354 : Formatter_NullTerminate(formatter);
306 : 22354 : }
307 : :
308 : 24831 : static inline bool Formatter_NeedsEscape(char value)
309 : : {
310 : : static const char QUOTE = '"';
311 : : static const char BACKSLASH = '\\';
312 : : static const char CLOSE_BRACKET = ']';
313 [ + + + + : 24831 : return (value == QUOTE) || (value == BACKSLASH) || (value == CLOSE_BRACKET);
+ + ]
314 : : }
315 : :
316 : 47185 : static inline bool Formatter_IsExhausted(const struct EscapedContext* context)
317 : : {
318 [ + + + + ]: 47185 : return context->Exhausted || (context->Source[context->SourcePos] == (char) '\0');
319 : : }
320 : :
321 : 178 : static inline void Formatter_WriteEscaped(struct EscapedContext* context)
322 : : {
323 : : static const char ESCAPE_PREFIX = '\\';
324 : : /* An escape pair on the wire ('\' + char) decodes back to the single
325 : : * character it was escaping - one byte in the reader's decoder buffer. */
326 : : static const size_t ESCAPED_CHARACTER_DECODED_LENGTH = 1;
327 [ + + ]: 178 : if (Formatter_Fits(context, ESCAPED_CHARACTER_DECODED_LENGTH))
328 : : {
329 : 177 : char escaped[] = {ESCAPE_PREFIX, context->Source[context->SourcePos]};
330 : 177 : Formatter_WriteContext(context, escaped, sizeof(escaped), 1, ESCAPED_CHARACTER_DECODED_LENGTH);
331 : : }
332 : : else
333 : : {
334 : 1 : Formatter_Exhaust(context);
335 : : }
336 : 178 : }
337 : :
338 : 24848 : static inline bool Formatter_Fits(const struct EscapedContext* context, size_t decodedAdvance)
339 : : {
340 [ + + + + ]: 24848 : return (decodedAdvance > 0U) && (decodedAdvance <= (context->MaxDecodedLength - context->DecodedLength));
341 : : }
342 : :
343 : : /* NOLINTBEGIN(bugprone-easily-swappable-parameters) -- see forward declaration */
344 : 24822 : static inline void Formatter_WriteContext(
345 : : struct EscapedContext* context,
346 : : const char* bytes,
347 : : size_t byteCount,
348 : : size_t sourceAdvance,
349 : : size_t decodedAdvance
350 : : )
351 : : {
352 : 24822 : Formatter_WriteBytes(context->Formatter, bytes, byteCount);
353 : 24822 : context->SourcePos += sourceAdvance;
354 : 24822 : context->DecodedLength += decodedAdvance;
355 : 24822 : }
356 : :
357 : : /* NOLINTEND(bugprone-easily-swappable-parameters) */
358 : :
359 : 9 : static inline void Formatter_Exhaust(struct EscapedContext* context)
360 : : {
361 : 9 : context->Exhausted = true;
362 : 9 : }
363 : :
364 : : /* Writes the source codepoint at sourcePos if it fits the decoded budget;
365 : : * otherwise falls back to Formatter_WriteReplacement (which emits U+FFFD or marks
366 : : * the context exhausted). Formatter_Fits also rejects invalid UTF-8 because
367 : : * Formatter_Utf8CodepointLength returns 0 for ill-formed sequences. */
368 : 24653 : static inline void Formatter_WriteCodepoint(struct EscapedContext* context)
369 : : {
370 : 24653 : size_t codepointLength = Formatter_Utf8CodepointLength(&context->Source[context->SourcePos]);
371 [ + + ]: 24653 : if (Formatter_Fits(context, codepointLength))
372 : : {
373 : 24636 : Formatter_WriteContext(
374 : : context,
375 : 24636 : &context->Source[context->SourcePos],
376 : : codepointLength,
377 : : codepointLength,
378 : : codepointLength
379 : : );
380 : : }
381 : : else
382 : : {
383 : 17 : Formatter_WriteReplacement(context);
384 : : }
385 : 24653 : }
386 : :
387 : 17 : static inline void Formatter_WriteReplacement(struct EscapedContext* context)
388 : : {
389 [ + + ]: 17 : if (Formatter_Fits(context, sizeof(REPLACEMENT_CHARACTER)))
390 : : {
391 : 9 : Formatter_WriteContext(
392 : : context,
393 : : REPLACEMENT_CHARACTER,
394 : : sizeof(REPLACEMENT_CHARACTER),
395 : : 1,
396 : : sizeof(REPLACEMENT_CHARACTER)
397 : : );
398 : : }
399 : : else
400 : : {
401 : 8 : Formatter_Exhaust(context);
402 : : }
403 : 17 : }
404 : :
405 : 22280 : void SolidSyslogFormatter_PrintUsAsciiString(
406 : : struct SolidSyslogFormatter* formatter,
407 : : const char* source,
408 : : size_t maxLength
409 : : )
410 : : {
411 : 22280 : size_t len = 0U;
412 : :
413 [ + + + + ]: 162884 : while ((len < maxLength) && (source[len] != '\0'))
414 : : {
415 : 140604 : Formatter_WritePrintableUsAsciiChar(formatter, source[len]);
416 : 140604 : len++;
417 : : }
418 : 22280 : Formatter_NullTerminate(formatter);
419 : 22280 : }
420 : :
421 : 140604 : static inline void Formatter_WritePrintableUsAsciiChar(struct SolidSyslogFormatter* formatter, char value)
422 : : {
423 [ + + ]: 140604 : if (Formatter_IsPrintableUsAscii(value))
424 : : {
425 : 140594 : Formatter_WriteChar(formatter, value);
426 : : }
427 : : else
428 : : {
429 : 10 : Formatter_WriteChar(formatter, NON_PRINTABLE_SUBSTITUTE);
430 : : }
431 : 140604 : }
432 : :
433 : 227204 : static inline bool Formatter_IsPrintableUsAscii(char value)
434 : : {
435 : : static const char LOWEST_PRINTABLE_US_ASCII = '!';
436 : : static const char HIGHEST_PRINTABLE_US_ASCII = '~';
437 [ + + + + ]: 227204 : return (value >= LOWEST_PRINTABLE_US_ASCII) && (value <= HIGHEST_PRINTABLE_US_ASCII);
438 : : }
439 : :
440 : 16923 : void SolidSyslogFormatter_Uint32(struct SolidSyslogFormatter* formatter, uint32_t value)
441 : : {
442 : 16923 : size_t digits = Formatter_CountDigits(value);
443 : 16923 : uint32_t divisor = 1;
444 : 16923 : uint32_t remaining = value;
445 : :
446 [ + + ]: 36693 : for (size_t i = 1; i < digits; i++)
447 : : {
448 : 19770 : divisor *= 10U;
449 : : }
450 : :
451 [ + + ]: 53616 : for (size_t i = 0U; i < digits; i++)
452 : : {
453 : 36693 : Formatter_WriteChar(formatter, Formatter_DigitToChar(remaining / divisor));
454 : 36693 : remaining %= divisor;
455 : 36693 : divisor /= 10U;
456 : : }
457 : 16923 : Formatter_NullTerminate(formatter);
458 : 16923 : }
459 : :
460 : 16923 : static size_t Formatter_CountDigits(uint32_t value)
461 : : {
462 : 16923 : size_t count = 1;
463 : 16923 : uint32_t remaining = value;
464 : :
465 [ + + ]: 36693 : while (remaining >= 10U)
466 : : {
467 : 19770 : count++;
468 : 19770 : remaining /= 10U;
469 : : }
470 : :
471 : 16923 : return count;
472 : : }
473 : :
474 : 92099 : static inline char Formatter_DigitToChar(uint32_t value)
475 : : {
476 : 92099 : return (char) ('0' + (value % 10U));
477 : : }
478 : :
479 : 27568 : void SolidSyslogFormatter_TwoDigit(struct SolidSyslogFormatter* formatter, uint32_t value)
480 : : {
481 : 27568 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 10U));
482 : 27568 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value));
483 : 27568 : Formatter_NullTerminate(formatter);
484 : 27568 : }
485 : :
486 : 27 : void SolidSyslogFormatter_FourDigit(struct SolidSyslogFormatter* formatter, uint32_t value)
487 : : {
488 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 1000U));
489 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 100U));
490 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 10U));
491 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value));
492 : 27 : Formatter_NullTerminate(formatter);
493 : 27 : }
494 : :
495 : 27 : void SolidSyslogFormatter_SixDigit(struct SolidSyslogFormatter* formatter, uint32_t value)
496 : : {
497 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 100000U));
498 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 10000U));
499 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 1000U));
500 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 100U));
501 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value / 10U));
502 : 27 : Formatter_WriteChar(formatter, Formatter_DigitToChar(value));
503 : 27 : Formatter_NullTerminate(formatter);
504 : 27 : }
505 : :
506 : 39200 : const char* SolidSyslogFormatter_AsFormattedBuffer(struct SolidSyslogFormatter* formatter)
507 : : {
508 : 39200 : Formatter_TrimTruncatedMultiByteTail(formatter);
509 : 39200 : return formatter->Buffer;
510 : : }
511 : :
512 : 39200 : static inline void Formatter_TrimTruncatedMultiByteTail(struct SolidSyslogFormatter* formatter)
513 : : {
514 : 39200 : char* buffer = formatter->Buffer;
515 : 39200 : size_t p = formatter->Position;
516 : 39200 : size_t trimFrom = p;
517 : :
518 [ + + + + ]: 76790 : if ((p >= 1U) &&
519 [ + + + + ]: 112766 : (SolidSyslogUtf8_IsTwoByteLead(buffer[p - 1U]) || SolidSyslogUtf8_IsThreeByteLead(buffer[p - 1U]) ||
520 : 37587 : SolidSyslogUtf8_IsFourByteLead(buffer[p - 1U])))
521 : : {
522 : 4 : trimFrom = p - 1U;
523 : : }
524 [ + + + + ]: 76759 : else if ((p >= 2U) &&
525 [ + + ]: 75123 : (SolidSyslogUtf8_IsThreeByteLead(buffer[p - 2U]) || SolidSyslogUtf8_IsFourByteLead(buffer[p - 2U])))
526 : : {
527 : 5 : trimFrom = p - 2U;
528 : : }
529 [ + + + + ]: 39191 : else if ((p >= 3U) && SolidSyslogUtf8_IsFourByteLead(buffer[p - 3U]))
530 : : {
531 : 2 : trimFrom = p - 3U;
532 : : }
533 : : else
534 : : {
535 : : /* tail does not look like a truncated multi-byte sequence - trim nothing */
536 : : }
537 [ + + ]: 39220 : for (size_t i = trimFrom; i < p; i++)
538 : : {
539 : 20 : buffer[i] = '\0';
540 : : }
541 : 39200 : }
542 : :
543 : 76964 : size_t SolidSyslogFormatter_Length(const struct SolidSyslogFormatter* formatter)
544 : : {
545 : 76964 : return formatter->Position;
546 : : }
547 : :
548 : : /* cppcheck-suppress constParameterPointer -- hands back the storage for the caller to write through. */
549 : 453 : char* SolidSyslogFormatter_Bytes(struct SolidSyslogFormatter* formatter)
550 : : {
551 : 453 : return formatter->Buffer;
552 : : }
|