Branch data Line data Source code
1 : : #include "mem.h"
2 : : #include <stdarg.h>
3 : : #include <stdio.h>
4 : : #include <wchar.h>
5 : :
6 : : /**
7 : : * @brief Render a narrow printf-style formatted string into a string descriptor
8 : : *
9 : : * Formats @p source_string together with the trailing variadic arguments and
10 : : * writes the result into @p destination, which must already be a string
11 : : * descriptor whose elements have `sizeof(char)` width. The format string is
12 : : * processed through `vsnprintf`
13 : : *
14 : : * Any previous visible content in @p destination is replaced. The format
15 : : * string must not point inside the destination allocation because self-aliasing
16 : : * of @p source_string is not currently supported by this helper
17 : : *
18 : : * @param destination String descriptor of `char` width
19 : : * @param source_string Narrow format string, followed
20 : : * by the variadic arguments that supply the format conversions
21 : : * @return `SUCCESS` on success; `FAILURE` otherwise
22 : : */
23 : 2172 : Return mem_formatted_string_char(
24 : : memory *destination,
25 : : const char *source_string,
26 : : ...)
27 : : {
28 : : /* Status returned by this function through provide()
29 : : Default value assumes successful completion */
30 : 2172 : Return status = SUCCESS;
31 : :
32 [ - + ]: 2172 : if(destination == NULL)
33 : : {
34 : 0 : report("Memory management; Destination must be non-NULL");
35 : 0 : provide(FAILURE);
36 : : }
37 : :
38 [ + + ]: 2172 : if(destination->is_string == false)
39 : : {
40 : 1 : report("Memory management; Destination must be a string descriptor");
41 : 1 : provide(FAILURE);
42 : : }
43 : :
44 : : /* A NULL format string cannot describe a formatting operation. Reject it
45 : : before reading the variadic arguments or modifying the destination */
46 [ + + ]: 2171 : if(source_string == NULL)
47 : : {
48 : 1 : report("Memory management; Format string must be non-NULL");
49 : 1 : provide(FAILURE);
50 : : }
51 : :
52 [ + + ]: 2170 : if(destination->single_element_size != sizeof(char))
53 : : {
54 : 2 : report("Memory management; Narrow formatted destination requires char elements");
55 : 2 : provide(FAILURE);
56 : : }
57 : :
58 : : va_list args;
59 : 2168 : va_start(args,source_string);
60 : :
61 : : va_list probe;
62 : 2168 : va_copy(probe,args);
63 : :
64 : 2168 : const int rendered = vsnprintf(NULL,0,source_string,probe);
65 : :
66 : 2168 : va_end(probe);
67 : :
68 : : /* A negative vsnprintf result is a runtime flow condition (encoding
69 : : issue in the format/args), not a programmer frame mistake. Exit
70 : : quietly with SUCCESS instead of reporting a frame error */
71 [ - + ]: 2168 : if(rendered < 0)
72 : : {
73 : 0 : va_end(args);
74 : 0 : provide(status);
75 : : }
76 : :
77 [ + - ]: 2168 : if(TRIUMPH & status)
78 : : {
79 : 2168 : run(m_resize(destination,(size_t)rendered + 1));
80 : :
81 [ + - ]: 2168 : if(TRIUMPH & status)
82 : : {
83 : 2168 : (void)vsnprintf((char *)destination->data,
84 : 2168 : (size_t)rendered + 1,
85 : : source_string,
86 : : args);
87 : :
88 : 2168 : destination->string_length = (size_t)rendered;
89 : : }
90 : : }
91 : :
92 : 2168 : va_end(args);
93 : :
94 : 2168 : provide(status);
95 : : }
96 : :
97 : : /**
98 : : * @brief Render a wide printf-style formatted string into a string descriptor
99 : : *
100 : : * Formats @p source_string together with the trailing variadic arguments and
101 : : * writes the result into @p destination, which must already be a string
102 : : * descriptor whose elements have `sizeof(wchar_t)` width. The format string
103 : : * is processed through `vswprintf`
104 : : *
105 : : * Any previous visible content in @p destination is replaced. The format
106 : : * string must not point inside the destination allocation because self-aliasing
107 : : * of @p source_string is not currently supported by this helper
108 : : *
109 : : * @param destination String descriptor of `wchar_t` width
110 : : * @param source_string Wide format string, followed by the variadic arguments
111 : : * that supply the format conversions
112 : : * @return `SUCCESS` on success; `FAILURE` otherwise
113 : : */
114 : 4 : Return mem_formatted_string_wchar(
115 : : memory *destination,
116 : : const wchar_t *source_string,
117 : : ...)
118 : : {
119 : : /* Status returned by this function through provide()
120 : : Default value assumes successful completion */
121 : 4 : Return status = SUCCESS;
122 : :
123 [ - + ]: 4 : if(destination == NULL)
124 : : {
125 : 0 : report("Memory management; Destination must be non-NULL");
126 : 0 : provide(FAILURE);
127 : : }
128 : :
129 [ - + ]: 4 : if(destination->is_string == false)
130 : : {
131 : 0 : report("Memory management; Destination must be a string descriptor");
132 : 0 : provide(FAILURE);
133 : : }
134 : :
135 : : /* A NULL format string cannot describe a formatting operation. Reject it
136 : : before reading the variadic arguments or modifying the destination */
137 [ + + ]: 4 : if(source_string == NULL)
138 : : {
139 : 1 : report("Memory management; Format string must be non-NULL");
140 : 1 : provide(FAILURE);
141 : : }
142 : :
143 [ - + ]: 3 : if(destination->single_element_size != sizeof(wchar_t))
144 : : {
145 : 0 : report("Memory management; Wide formatted destination requires wchar_t elements");
146 : 0 : provide(FAILURE);
147 : : }
148 : :
149 : : va_list args;
150 : 3 : va_start(args,source_string);
151 : :
152 : : /* Wide-character path. Unlike vsnprintf, the standard does not
153 : : define a measure-only mode for vswprintf: passing a NULL buffer
154 : : or a zero size is not portable, and a buffer that is too small
155 : : simply returns a negative value with no hint about how much
156 : : room would have been needed. Without a measurement pass the
157 : : only portable way to find the right size is to render
158 : : speculatively and grow on failure.
159 : :
160 : : Strategy: start with a reasonable initial capacity, try to
161 : : render in place, and if vswprintf reports the buffer was too
162 : : small, double the capacity and retry. The doubling loop is
163 : : bounded both by an iteration limit and by SIZE_MAX/2 so a
164 : : pathological format cannot drive the descriptor into an
165 : : unbounded growth spin. va_copy creates a fresh argument list
166 : : for each retry because vswprintf consumes the va_list on every
167 : : call. After a successful render the destination is resized
168 : : down to the exact length plus one terminator, so the leftover
169 : : slack from the last doubling is not retained */
170 : 3 : size_t guessed_length = 128;
171 : 3 : const int max_doublings = 20;
172 : 3 : int rendered = -1;
173 : :
174 [ + - ]: 4 : for(int iteration = 0; iteration <= max_doublings; ++iteration)
175 : : {
176 : 4 : run(m_resize(destination,guessed_length + 1));
177 : :
178 [ - + ]: 4 : if(CRITICAL & status)
179 : : {
180 : 3 : break;
181 : : }
182 : :
183 : : va_list probe;
184 : 4 : va_copy(probe,args);
185 : :
186 : 4 : rendered = vswprintf((wchar_t *)destination->data,
187 : : guessed_length + 1,
188 : : source_string,
189 : : probe);
190 : :
191 : 4 : va_end(probe);
192 : :
193 [ + + ]: 4 : if(rendered >= 0)
194 : : {
195 : 3 : break;
196 : : }
197 : :
198 [ + - - + ]: 1 : if(iteration == max_doublings || guessed_length > SIZE_MAX / 2)
199 : : {
200 : 0 : report("Memory management; vswprintf failed after maximum growth iterations");
201 : 0 : status = FAILURE;
202 : 0 : break;
203 : : }
204 : :
205 : 1 : guessed_length *= 2;
206 : : }
207 : :
208 [ + - + - ]: 3 : if((TRIUMPH & status) && rendered >= 0)
209 : : {
210 : : /* Tell the descriptor about the rendered length BEFORE the trim
211 : : resize. String-mode shrink uses string_length to decide how
212 : : much of the visible payload to preserve, so leaving the old
213 : : value in place would make m_resize treat the just-written
214 : : text as overflow and overwrite its prefix with a terminator */
215 : 3 : destination->string_length = (size_t)rendered;
216 : :
217 : 3 : run(m_resize(destination,(size_t)rendered + 1));
218 : : }
219 : :
220 : 3 : va_end(args);
221 : :
222 : 3 : provide(status);
223 : : }
|