Line data Source code
1 : /* File: utf8stringbuf.inl; Copyright and License: see below */
2 :
3 : #include "utf8stringbuf/utf8codepoint.h"
4 : #include <string.h>
5 : #include <stdio.h>
6 : #include <stdint.h>
7 :
8 : #ifdef __cplusplus
9 : extern "C" {
10 : #endif
11 :
12 : /*!
13 : * \enum utf8stringbuf_bool_enum
14 : * \private
15 : */
16 : /* enumeration for true and false or success and failure */
17 : enum utf8stringbuf_bool_enum {UTF8STRINGBUF_FALSE=0, UTF8STRINGBUF_TRUE=1,};
18 :
19 : /*!
20 : * \enum utf8stringbuf_search_enum
21 : * \private
22 : */
23 : /* enumeration for search pattern not found */
24 : enum utf8stringbuf_search_enum {UTF8STRINGBUF_NOT_FOUND=-1,};
25 :
26 : /*!
27 : * \var utf8stringbuf_private_format_signed_64_bit_int
28 : * \private
29 : */
30 : /* a character array containing nothing but a single zero */
31 : extern const char *utf8stringbuf_private_format_signed_64_bit_int;
32 :
33 : /*!
34 : * \var utf8stringbuf_private_format_64_bit_hex
35 : * \private
36 : */
37 : /* a character array containing nothing but a single zero */
38 : extern const char *utf8stringbuf_private_format_64_bit_hex;
39 :
40 : /*!
41 : * \var utf8stringbuf_private_empty_buf
42 : * \private
43 : */
44 : /* a character array containing nothing but a single zero */
45 : extern char utf8stringbuf_private_empty_buf[1];
46 :
47 : /*!
48 : * \fn utf8_string_buf_private_make_null_termination( utf8stringbuf_t *this_ )
49 : * \private
50 : * \return new, truncated length of the string
51 : */
52 : /* function to make a buffer null terminated while ensuring that all utf8 character sequences are valid */
53 : extern unsigned int utf8_string_buf_private_make_null_termination( utf8stringbuf_t *this_ );
54 :
55 15 : static inline utf8stringbuf_t utf8stringbuf( char *that )
56 : {
57 : utf8stringbuf_t result;
58 15 : if ( that == NULL )
59 : {
60 1 : result.size = 1;
61 1 : result.buf = utf8stringbuf_private_empty_buf;
62 : }
63 : else
64 : {
65 14 : result.size = strlen( that )+1;
66 14 : result.buf = that;
67 : }
68 15 : return result;
69 : }
70 :
71 42301 : static inline utf8stringbuf_t utf8stringbuf_new( char *buf, size_t size )
72 : {
73 : utf8stringbuf_t result;
74 42301 : if (( buf == NULL )||(size==0))
75 : {
76 1 : result.size = 1;
77 1 : result.buf = utf8stringbuf_private_empty_buf;
78 : }
79 : else
80 : {
81 42300 : result.size = size;
82 42300 : result.buf = buf;
83 : }
84 42301 : return result;
85 : }
86 :
87 7778 : static inline void utf8stringbuf_clear( utf8stringbuf_t *this_ )
88 : {
89 7778 : memset( (*this_).buf, '\0', (*this_).size );
90 7778 : }
91 :
92 20453 : static inline char* utf8stringbuf_get_string( const utf8stringbuf_t *this_ )
93 : {
94 20453 : return (*this_).buf;
95 : }
96 :
97 1174 : static inline size_t utf8stringbuf_get_size( const utf8stringbuf_t *this_ )
98 : {
99 1174 : return (*this_).size;
100 : }
101 :
102 8298 : static inline unsigned int utf8stringbuf_get_length( const utf8stringbuf_t *this_ )
103 : {
104 : unsigned int lenResult;
105 8298 : lenResult = strlen( (*this_).buf );
106 8298 : return lenResult;
107 : }
108 :
109 1 : static inline utf8stringview_t utf8stringbuf_get_view( const utf8stringbuf_t *this_ )
110 : {
111 : utf8stringview_t result;
112 1 : assert( NULL != (*this_).buf );
113 1 : const utf8error_t err = utf8stringview_init( &result, (*this_).buf, strlen( (*this_).buf ) );
114 1 : assert( UTF8ERROR_SUCCESS == err );
115 : (void) err; /* do not warn on unused value */
116 1 : return result;
117 : }
118 :
119 5173 : static inline int utf8stringbuf_equals_str( const utf8stringbuf_t *this_, const char *that )
120 : {
121 5173 : int cmpResult = -1;
122 5173 : if ( that != NULL )
123 : {
124 5171 : cmpResult = strcmp( (*this_).buf, that );
125 : }
126 5173 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
127 : }
128 :
129 : static inline int utf8stringbuf_equals_view( const utf8stringbuf_t *this_, const utf8stringview_t *that )
130 : {
131 : assert( that != NULL );
132 : int result;
133 : const size_t len = utf8stringview_get_length( that );
134 : if ( len == utf8stringbuf_get_length(this_) )
135 : {
136 : if ( ( len == 0 )/*&&( (*this_).length == 0 )*/)
137 : {
138 : result = 1;
139 : }
140 : else
141 : {
142 : result = ( 0 == memcmp ( (*this_).buf, utf8stringview_get_start(that), len ) ) ? 1 : 0;
143 : }
144 : }
145 : else
146 : {
147 : result = 0;
148 : }
149 : return result;
150 : }
151 :
152 4 : static inline int utf8stringbuf_equals_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that )
153 : {
154 4 : int cmpResult = -1;
155 4 : cmpResult = strcmp( (*this_).buf, (*that).buf );
156 4 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
157 : }
158 :
159 : static inline int utf8stringbuf_find_first_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t pattern )
160 : {
161 : int result = UTF8STRINGBUF_NOT_FOUND;
162 : const char *ptrResult = strstr( (*this_).buf, pattern.buf );
163 : if ( ptrResult != NULL )
164 : {
165 : result = (int) (ptrResult - (*this_).buf);
166 : }
167 : return result;
168 : }
169 :
170 6 : static inline int utf8stringbuf_starts_with_str( const utf8stringbuf_t *this_, const char *that )
171 : {
172 6 : int cmpResult = -1;
173 6 : if ( that != NULL )
174 : {
175 5 : unsigned int thatLen = strlen( that );
176 5 : cmpResult = strncmp( (*this_).buf, that, thatLen );
177 : }
178 6 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
179 : }
180 :
181 5 : static inline int utf8stringbuf_starts_with_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that )
182 : {
183 5 : int cmpResult = -1;
184 5 : unsigned int thatLen = strlen( (*that).buf );
185 5 : cmpResult = strncmp( (*this_).buf, (*that).buf, thatLen );
186 5 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
187 : }
188 :
189 6 : static inline int utf8stringbuf_ends_with_str( const utf8stringbuf_t *this_, const char *that )
190 : {
191 6 : int cmpResult = -1;
192 6 : if ( that != NULL )
193 : {
194 5 : unsigned int thatLen = strlen( that );
195 5 : unsigned int thisLen = strlen( (*this_).buf );
196 5 : if ( thatLen <= thisLen )
197 : {
198 4 : cmpResult = memcmp( &((*this_).buf[thisLen-thatLen]), that, thatLen );
199 : }
200 : }
201 6 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
202 : }
203 :
204 5 : static inline int utf8stringbuf_ends_with_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that )
205 : {
206 5 : int cmpResult = -1;
207 5 : unsigned int thatLen = strlen( (*that).buf );
208 5 : unsigned int thisLen = strlen( (*this_).buf );
209 5 : if ( thatLen <= thisLen )
210 : {
211 4 : cmpResult = memcmp( &((*this_).buf[thisLen-thatLen]), (*that).buf, thatLen );
212 : }
213 5 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
214 : }
215 :
216 4 : static inline utf8error_t utf8stringbuf_copy_buf( utf8stringbuf_t *this_, const utf8stringbuf_t *original )
217 : {
218 4 : utf8error_t complete = UTF8ERROR_SUCCESS;
219 : #if __GNUC__ >= 8
220 : #pragma GCC diagnostic push
221 : #pragma GCC diagnostic ignored "-Wstringop-truncation"
222 : #endif
223 4 : strncpy( (*this_).buf, (*original).buf, (*this_).size );
224 : #if __GNUC__ >= 8
225 : #pragma GCC diagnostic pop
226 : #endif
227 4 : if ( (*this_).buf[(*this_).size-1] != '\0' )
228 : {
229 2 : utf8_string_buf_private_make_null_termination( this_ );
230 2 : complete = UTF8ERROR_TRUNCATED;
231 : }
232 4 : return complete;
233 : }
234 :
235 34250 : static inline utf8error_t utf8stringbuf_copy_str( utf8stringbuf_t *this_, const char *original )
236 : {
237 34250 : utf8error_t complete = UTF8ERROR_SUCCESS;
238 34250 : if ( original == NULL )
239 : {
240 1 : (*this_).buf[0] = '\0';
241 1 : complete = UTF8ERROR_NULL_PARAM;
242 : }
243 : else
244 : {
245 : #if __GNUC__ >= 8
246 : #pragma GCC diagnostic push
247 : #pragma GCC diagnostic ignored "-Wstringop-truncation"
248 : #endif
249 34249 : strncpy( (*this_).buf, original, (*this_).size );
250 : #if __GNUC__ >= 8
251 : #pragma GCC diagnostic pop
252 : #endif
253 34249 : if ( (*this_).buf[(*this_).size-1] != '\0' )
254 : {
255 63 : utf8_string_buf_private_make_null_termination( this_ );
256 63 : complete = UTF8ERROR_TRUNCATED;
257 : }
258 : }
259 34250 : return complete;
260 : }
261 :
262 38 : static inline utf8error_t utf8stringbuf_copy_view( utf8stringbuf_t *this_, const utf8stringview_t *original )
263 : {
264 38 : assert( original != NULL );
265 38 : utf8error_t result = UTF8ERROR_SUCCESS;
266 :
267 38 : const size_t origLen = utf8stringview_get_length( original );
268 38 : if ( origLen < (*this_).size )
269 : {
270 36 : memcpy( &((*this_).buf[0]), utf8stringview_get_start( original ), origLen );
271 36 : (*this_).buf[origLen] = '\0';
272 : }
273 : else
274 : {
275 2 : if ( (*this_).size > 0 )
276 : {
277 2 : memcpy( &((*this_).buf[0]), utf8stringview_get_start( original ), ((*this_).size-1) );
278 : }
279 : else
280 : {
281 : /* buffer non-existant */
282 : }
283 2 : utf8_string_buf_private_make_null_termination( this_ );
284 2 : result = UTF8ERROR_TRUNCATED;
285 : }
286 :
287 38 : return result;
288 : }
289 :
290 14067 : static inline utf8error_t utf8stringbuf_append_str( utf8stringbuf_t *this_, const char *appendix )
291 : {
292 14067 : utf8error_t result = UTF8ERROR_SUCCESS;
293 14067 : if ( appendix == NULL )
294 : {
295 1 : result = UTF8ERROR_NULL_PARAM;
296 : }
297 : else
298 : {
299 14066 : const size_t start = strlen( (*this_).buf );
300 :
301 14066 : const size_t appLen = strlen( appendix );
302 14066 : if ( ( start + appLen ) < (*this_).size )
303 : {
304 : /* gcc may not reliably evaluate the if condition above */
305 : /* possibly related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329 */
306 : /* or https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89689 */
307 14052 : assert( ( start + appLen + 1 ) <= (*this_).size );
308 : #if __GNUC__ >= 8
309 : #pragma GCC diagnostic push
310 : #pragma GCC diagnostic ignored "-Wstringop-overflow"
311 : #endif
312 14052 : memcpy( &((*this_).buf[start]), appendix, appLen+1 );
313 : #if __GNUC__ >= 8
314 : #pragma GCC diagnostic pop
315 : #endif
316 : }
317 : else
318 : {
319 14 : const size_t appPartLen = ((*this_).size-start)-1; /* cannot be negative */
320 14 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
321 : {
322 13 : memcpy( &((*this_).buf[start]), appendix, appPartLen );
323 : }
324 : else
325 : {
326 : /* buffer full */
327 : }
328 14 : utf8_string_buf_private_make_null_termination( this_ );
329 14 : result = UTF8ERROR_TRUNCATED;
330 : }
331 :
332 : /* For the standard use case, strlen and memcpy are faster than strncpy; strncat does not provide an error */
333 : }
334 14067 : return result;
335 : }
336 :
337 3 : static inline utf8error_t utf8stringbuf_append_buf( utf8stringbuf_t *this_, const utf8stringbuf_t *appendix ) {
338 3 : utf8error_t result = UTF8ERROR_SUCCESS;
339 3 : const size_t start = strlen( (*this_).buf );
340 :
341 3 : const size_t appLen = strlen( (*appendix).buf );
342 3 : if ( start + appLen < (*this_).size )
343 : {
344 1 : memcpy( &((*this_).buf[start]), (*appendix).buf, appLen+1 );
345 : }
346 : else
347 : {
348 2 : const size_t appPartLen = ((*this_).size-start)-1;
349 2 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
350 : {
351 1 : memcpy( &((*this_).buf[start]), (*appendix).buf, appPartLen );
352 : }
353 : else
354 : {
355 : /* buffer full */
356 : }
357 2 : utf8_string_buf_private_make_null_termination( this_ );
358 2 : result = UTF8ERROR_TRUNCATED;
359 : }
360 :
361 : /* For the standard use case, strlen and memcpy are faster than strncpy; strncat does not provide an error */
362 3 : return result;
363 : }
364 :
365 3416 : static inline utf8error_t utf8stringbuf_append_int( utf8stringbuf_t *this_, const int64_t appendix )
366 : {
367 : char numberStr[21]; /* this is sufficient for signed 64 bit integers: -9223372036854775806 */
368 : /* Note: snprintf is not available on every OS */
369 3416 : sprintf( numberStr, utf8stringbuf_private_format_signed_64_bit_int, appendix );
370 3416 : return utf8stringbuf_append_str( this_, numberStr );
371 : }
372 :
373 3 : static inline utf8error_t utf8stringbuf_append_hex( utf8stringbuf_t *this_, const uint64_t appendix )
374 : {
375 : char numberStr[17]; /* this is sufficient for 64 bit integers */
376 : /* Note: snprintf is not available on every OS */
377 3 : sprintf( numberStr, utf8stringbuf_private_format_64_bit_hex, appendix );
378 3 : return utf8stringbuf_append_str( this_, numberStr );
379 : }
380 :
381 61 : static inline utf8stringbuf_t utf8stringbuf_get_end( utf8stringbuf_t *this_ )
382 : {
383 61 : unsigned int this_Length = utf8stringbuf_get_length( this_ );
384 61 : return utf8stringbuf_new( &((*this_).buf[this_Length]), (*this_).size - this_Length );
385 : }
386 :
387 9 : static inline utf8error_t utf8stringbuf_append_view( utf8stringbuf_t *this_, const utf8stringview_t *appendix )
388 : {
389 9 : assert( appendix != NULL );
390 9 : utf8error_t result = UTF8ERROR_SUCCESS;
391 9 : const size_t start = strlen( (*this_).buf );
392 :
393 9 : const size_t appLen = utf8stringview_get_length( appendix );
394 9 : if ( start + appLen < (*this_).size )
395 : {
396 7 : memcpy( &((*this_).buf[start]), utf8stringview_get_start( appendix ), appLen );
397 7 : (*this_).buf[start+appLen] = '\0';
398 : }
399 : else
400 : {
401 2 : const size_t appPartLen = ((*this_).size-start)-1;
402 2 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
403 : {
404 1 : memcpy( &((*this_).buf[start]), utf8stringview_get_start( appendix ), appPartLen );
405 : }
406 : else
407 : {
408 : /* buffer full */
409 : }
410 2 : utf8_string_buf_private_make_null_termination( this_ );
411 2 : result = UTF8ERROR_TRUNCATED;
412 : }
413 :
414 9 : return result;
415 : }
416 :
417 : #ifdef __cplusplus
418 : }
419 : #endif
420 :
421 :
422 : /*
423 : * Copyright 2012-2025 Andreas Warnke
424 : *
425 : * Licensed under the Apache License, Version 2.0 (the "License");
426 : * you may not use this file except in compliance with the License.
427 : * You may obtain a copy of the License at
428 : *
429 : * http://www.apache.org/licenses/LICENSE-2.0
430 : *
431 : * Unless required by applicable law or agreed to in writing, software
432 : * distributed under the License is distributed on an "AS IS" BASIS,
433 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
434 : * See the License for the specific language governing permissions and
435 : * limitations under the License.
436 : */
|