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 "SolidSyslogSdValuePrivate.h"
6 : :
7 : : #include <stddef.h>
8 : : #include <stdint.h>
9 : :
10 : : #include "SolidSyslogFormatter.h"
11 : : #include "SolidSyslogUtf8.h"
12 : :
13 : : static size_t SdValue_DrainPending(struct SolidSyslogSdValue* value, const char* source);
14 : : static void SdValue_WriteBody(struct SolidSyslogSdValue* value, const char* source);
15 : : static inline size_t SdValue_ExpectedLength(char lead);
16 : : static inline void SdValue_EmitUnit(struct SolidSyslogSdValue* value, const char* bytes, size_t count);
17 : : static inline void SdValue_Hold(struct SolidSyslogSdValue* value, const char* bytes, size_t count);
18 : : static inline void SdValue_FlushPendingAsReplacement(struct SolidSyslogSdValue* value);
19 : : static inline void SdValue_FlushPendingIfHeld(struct SolidSyslogSdValue* value);
20 : :
21 : 8286 : void SolidSyslogSdValue_FromFormatter(struct SolidSyslogSdValue* value, struct SolidSyslogFormatter* formatter)
22 : : {
23 : 8286 : value->Formatter = formatter;
24 : 8286 : value->PendingCount = 0;
25 : 8286 : }
26 : :
27 : 1406 : void SolidSyslogSdValue_String(struct SolidSyslogSdValue* value, const char* source)
28 : : {
29 : 1406 : size_t bodyStart = SdValue_DrainPending(value, source);
30 : 1406 : SdValue_WriteBody(value, &source[bodyStart]);
31 : 1406 : }
32 : :
33 : : /* Completes a held trailing sequence from the leading continuation bytes of
34 : : * source, returning the number of source bytes consumed doing so. A completed
35 : : * codepoint is emitted; a sequence interrupted by a non-continuation byte is
36 : : * replaced with one U+FFFD; a sequence still incomplete at end-of-source stays
37 : : * held for the next call. */
38 : 1406 : static size_t SdValue_DrainPending(struct SolidSyslogSdValue* value, const char* source)
39 : : {
40 : 1406 : size_t consumed = 0U;
41 [ + + ]: 1406 : if (value->PendingCount > 0U)
42 : : {
43 : 7 : size_t expected = SdValue_ExpectedLength(value->Pending[0]);
44 [ + + + + ]: 14 : while ((value->PendingCount < expected) && SolidSyslogUtf8_IsContinuationByte(source[consumed]))
45 : : {
46 : 7 : value->Pending[value->PendingCount] = source[consumed];
47 : 7 : value->PendingCount++;
48 : 7 : consumed++;
49 : : }
50 [ + + ]: 7 : if (value->PendingCount == expected)
51 : : {
52 : 4 : SdValue_EmitUnit(value, value->Pending, value->PendingCount);
53 : 4 : value->PendingCount = 0;
54 : : }
55 [ + + ]: 3 : else if (source[consumed] != '\0')
56 : : {
57 : 1 : SdValue_FlushPendingAsReplacement(value);
58 : : }
59 : : else
60 : : {
61 : : /* source exhausted mid-sequence - keep the tail for the next call */
62 : : }
63 : : }
64 : 1406 : return consumed;
65 : : }
66 : :
67 : : /* Walks source one UTF-8 unit at a time, emitting each complete unit through
68 : : * the formatter's escaper. A unit whose continuation bytes run out at
69 : : * end-of-source is held for the next call rather than emitted. */
70 : 1406 : static void SdValue_WriteBody(struct SolidSyslogSdValue* value, const char* source)
71 : : {
72 : 1406 : size_t i = 0U;
73 [ + + ]: 23492 : while (source[i] != '\0')
74 : : {
75 : 22086 : size_t expected = SdValue_ExpectedLength(source[i]);
76 : 22086 : size_t avail = 1;
77 [ + + + + ]: 22088 : while ((avail < expected) && SolidSyslogUtf8_IsContinuationByte(source[i + avail]))
78 : : {
79 : 2 : avail++;
80 : : }
81 [ + + + + ]: 22086 : if ((avail < expected) && (source[i + avail] == '\0'))
82 : : {
83 : 8 : SdValue_Hold(value, &source[i], avail);
84 : : }
85 : : else
86 : : {
87 : 22078 : SdValue_EmitUnit(value, &source[i], avail);
88 : : }
89 : 22086 : i += avail;
90 : : }
91 : 1406 : }
92 : :
93 : : /* The number of bytes a UTF-8 sequence starting with lead occupies, by its
94 : : * top-bit structure alone. ASCII, escapable, orphan-continuation and invalid
95 : : * leads are single-byte units; validity is left to the formatter's escaper. */
96 : 22093 : static inline size_t SdValue_ExpectedLength(char lead)
97 : : {
98 : 22093 : size_t length = 1;
99 [ + + ]: 22093 : if (SolidSyslogUtf8_IsTwoByteLead(lead))
100 : : {
101 : 9 : length = 2;
102 : : }
103 [ + + ]: 22084 : else if (SolidSyslogUtf8_IsThreeByteLead(lead))
104 : : {
105 : 2 : length = 3;
106 : : }
107 [ + + ]: 22082 : else if (SolidSyslogUtf8_IsFourByteLead(lead))
108 : : {
109 : 6 : length = 4;
110 : : }
111 : : else
112 : : {
113 : : /* single-byte unit */
114 : : }
115 : 22093 : return length;
116 : : }
117 : :
118 : : /* Emits one UTF-8 unit through the formatter's escaper - the single source of
119 : : * truth for escaping ('"', '\\', ']') and per-byte ill-formed substitution. */
120 : 22082 : static inline void SdValue_EmitUnit(struct SolidSyslogSdValue* value, const char* bytes, size_t count)
121 : : {
122 : : char unit[SDVALUE_MAX_CODEPOINT_BYTES + 1U];
123 [ + + ]: 44173 : for (size_t i = 0U; i < count; i++)
124 : : {
125 : 22091 : unit[i] = bytes[i];
126 : : }
127 : 22082 : unit[count] = '\0';
128 : 22082 : SolidSyslogFormatter_EscapedString(value->Formatter, unit, SIZE_MAX);
129 : 22082 : }
130 : :
131 : 8 : static inline void SdValue_Hold(struct SolidSyslogSdValue* value, const char* bytes, size_t count)
132 : : {
133 [ + + ]: 17 : for (size_t i = 0U; i < count; i++)
134 : : {
135 : 9 : value->Pending[i] = bytes[i];
136 : : }
137 : 8 : value->PendingCount = count;
138 : 8 : }
139 : :
140 : : /* Re-uses the formatter's own ill-formed-byte substitution: a lone
141 : : * continuation byte decodes to exactly one U+FFFD, so the held tail is replaced
142 : : * without SdValue duplicating the replacement character. */
143 : 4 : static inline void SdValue_FlushPendingAsReplacement(struct SolidSyslogSdValue* value)
144 : : {
145 : : static const char LONE_CONTINUATION[] = {'\x80', '\0'};
146 : 4 : SolidSyslogFormatter_EscapedString(value->Formatter, LONE_CONTINUATION, SIZE_MAX);
147 : 4 : value->PendingCount = 0;
148 : 4 : }
149 : :
150 : 255 : void SolidSyslogSdValue_BoundedString(struct SolidSyslogSdValue* value, const char* source, size_t maxDecodedLength)
151 : : {
152 : 255 : SdValue_FlushPendingIfHeld(value);
153 : 255 : SolidSyslogFormatter_EscapedString(value->Formatter, source, maxDecodedLength);
154 : 255 : }
155 : :
156 : 6636 : void SolidSyslogSdValue_Uint32(struct SolidSyslogSdValue* value, uint32_t number)
157 : : {
158 : 6636 : SdValue_FlushPendingIfHeld(value);
159 : 6636 : SolidSyslogFormatter_Uint32(value->Formatter, number);
160 : 6636 : }
161 : :
162 : 8259 : void SolidSyslogSdValue_Close(struct SolidSyslogSdValue* value)
163 : : {
164 : 8259 : SdValue_FlushPendingIfHeld(value);
165 : 8259 : }
166 : :
167 : : /* A held incomplete UTF-8 tail is the dangling state of a prior
168 : : * SolidSyslogSdValue_String call. Any non-SolidSyslogSdValue_String write (or
169 : : * close) terminates it with one U+FFFD before its own output, so the tail is
170 : : * never reordered after, or merged into, that output. Only consecutive
171 : : * SolidSyslogSdValue_String calls continue a tail (streaming continuation). */
172 : 15150 : static inline void SdValue_FlushPendingIfHeld(struct SolidSyslogSdValue* value)
173 : : {
174 [ + + ]: 15150 : if (value->PendingCount > 0U)
175 : : {
176 : 3 : SdValue_FlushPendingAsReplacement(value);
177 : : }
178 : 15150 : }
|