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 42247 : static inline utf8stringbuf_t utf8stringbuf_new( char *buf, size_t size )
72 : {
73 : utf8stringbuf_t result;
74 42247 : if (( buf == NULL )||(size==0))
75 : {
76 1 : result.size = 1;
77 1 : result.buf = utf8stringbuf_private_empty_buf;
78 : }
79 : else
80 : {
81 42246 : result.size = size;
82 42246 : result.buf = buf;
83 : }
84 42247 : return result;
85 : }
86 :
87 7764 : static inline void utf8stringbuf_clear( utf8stringbuf_t *this_ )
88 : {
89 7764 : memset( (*this_).buf, '\0', (*this_).size );
90 7764 : }
91 :
92 20353 : static inline char* utf8stringbuf_get_string( const utf8stringbuf_t *this_ )
93 : {
94 20353 : 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 8288 : static inline unsigned int utf8stringbuf_get_length( const utf8stringbuf_t *this_ )
103 : {
104 : unsigned int lenResult;
105 8288 : lenResult = strlen( (*this_).buf );
106 8288 : 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 5171 : cmpResult = strcmp( (*this_).buf, that );
124 : }
125 5173 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
126 : }
127 :
128 : static inline int utf8stringbuf_equals_view( const utf8stringbuf_t *this_, const utf8stringview_t *that )
129 : {
130 : assert( that != NULL );
131 : int result;
132 : const size_t len = utf8stringview_get_length( that );
133 : if ( len == utf8stringbuf_get_length(this_) )
134 : {
135 : if ( ( len == 0 )/*&&( (*this_).length == 0 )*/)
136 : {
137 : result = 1;
138 : }
139 : else
140 : {
141 : result = ( 0 == memcmp ( (*this_).buf, utf8stringview_get_start(that), len ) ) ? 1 : 0;
142 : }
143 : }
144 : else
145 : {
146 : result = 0;
147 : }
148 : return result;
149 : }
150 :
151 4 : static inline int utf8stringbuf_equals_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that )
152 : {
153 4 : int cmpResult = -1;
154 4 : cmpResult = strcmp( (*this_).buf, (*that).buf );
155 4 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
156 : }
157 :
158 : static inline int utf8stringbuf_find_first_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t pattern )
159 : {
160 : int result = UTF8STRINGBUF_NOT_FOUND;
161 : const char *ptrResult = strstr( (*this_).buf, pattern.buf );
162 : if ( ptrResult != NULL ) {
163 : result = (int) (ptrResult - (*this_).buf);
164 : }
165 : return result;
166 : }
167 :
168 6 : static inline int utf8stringbuf_starts_with_str( const utf8stringbuf_t *this_, const char *that )
169 : {
170 6 : int cmpResult = -1;
171 6 : if ( that != NULL ) {
172 5 : unsigned int thatLen = strlen( that );
173 5 : cmpResult = strncmp( (*this_).buf, that, thatLen );
174 : }
175 6 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
176 : }
177 :
178 5 : static inline int utf8stringbuf_starts_with_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that ) {
179 5 : int cmpResult = -1;
180 5 : unsigned int thatLen = strlen( (*that).buf );
181 5 : cmpResult = strncmp( (*this_).buf, (*that).buf, thatLen );
182 5 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
183 : }
184 :
185 6 : static inline int utf8stringbuf_ends_with_str( const utf8stringbuf_t *this_, const char *that ) {
186 6 : int cmpResult = -1;
187 6 : if ( that != NULL ) {
188 5 : unsigned int thatLen = strlen( that );
189 5 : unsigned int thisLen = strlen( (*this_).buf );
190 5 : if ( thatLen <= thisLen ) {
191 4 : cmpResult = memcmp( &((*this_).buf[thisLen-thatLen]), that, thatLen );
192 : }
193 : }
194 6 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
195 : }
196 :
197 5 : static inline int utf8stringbuf_ends_with_buf( const utf8stringbuf_t *this_, const utf8stringbuf_t *that ) {
198 5 : int cmpResult = -1;
199 5 : unsigned int thatLen = strlen( (*that).buf );
200 5 : unsigned int thisLen = strlen( (*this_).buf );
201 5 : if ( thatLen <= thisLen ) {
202 4 : cmpResult = memcmp( &((*this_).buf[thisLen-thatLen]), (*that).buf, thatLen );
203 : }
204 5 : return ( cmpResult == 0 ) ? UTF8STRINGBUF_TRUE : UTF8STRINGBUF_FALSE;
205 : }
206 :
207 4 : static inline utf8error_t utf8stringbuf_copy_buf( utf8stringbuf_t *this_, const utf8stringbuf_t *original ) {
208 4 : utf8error_t complete = UTF8ERROR_SUCCESS;
209 : #if __GNUC__ >= 8
210 : #pragma GCC diagnostic push
211 : #pragma GCC diagnostic ignored "-Wstringop-truncation"
212 : #endif
213 4 : strncpy( (*this_).buf, (*original).buf, (*this_).size );
214 : #if __GNUC__ >= 8
215 : #pragma GCC diagnostic pop
216 : #endif
217 4 : if ( (*this_).buf[(*this_).size-1] != '\0' ) {
218 2 : utf8_string_buf_private_make_null_termination( this_ );
219 2 : complete = UTF8ERROR_TRUNCATED;
220 : }
221 4 : return complete;
222 : }
223 :
224 34158 : static inline utf8error_t utf8stringbuf_copy_str( utf8stringbuf_t *this_, const char *original ) {
225 34158 : utf8error_t complete = UTF8ERROR_SUCCESS;
226 34158 : if ( original == NULL ) {
227 1 : (*this_).buf[0] = '\0';
228 1 : complete = UTF8ERROR_NULL_PARAM;
229 : }
230 : else {
231 : #if __GNUC__ >= 8
232 : #pragma GCC diagnostic push
233 : #pragma GCC diagnostic ignored "-Wstringop-truncation"
234 : #endif
235 34157 : strncpy( (*this_).buf, original, (*this_).size );
236 : #if __GNUC__ >= 8
237 : #pragma GCC diagnostic pop
238 : #endif
239 34157 : if ( (*this_).buf[(*this_).size-1] != '\0' ) {
240 63 : utf8_string_buf_private_make_null_termination( this_ );
241 63 : complete = UTF8ERROR_TRUNCATED;
242 : }
243 : }
244 34158 : return complete;
245 : }
246 :
247 38 : static inline utf8error_t utf8stringbuf_copy_view( utf8stringbuf_t *this_, const utf8stringview_t *original )
248 : {
249 38 : assert( original != NULL );
250 38 : utf8error_t result = UTF8ERROR_SUCCESS;
251 :
252 38 : const size_t origLen = utf8stringview_get_length( original );
253 38 : if ( origLen < (*this_).size ) {
254 36 : memcpy( &((*this_).buf[0]), utf8stringview_get_start( original ), origLen );
255 36 : (*this_).buf[origLen] = '\0';
256 : }
257 : else {
258 2 : if ( (*this_).size > 0 )
259 : {
260 2 : memcpy( &((*this_).buf[0]), utf8stringview_get_start( original ), ((*this_).size-1) );
261 : }
262 : else
263 : {
264 : /* buffer non-existant */
265 : }
266 2 : utf8_string_buf_private_make_null_termination( this_ );
267 2 : result = UTF8ERROR_TRUNCATED;
268 : }
269 :
270 38 : return result;
271 : }
272 :
273 13951 : static inline utf8error_t utf8stringbuf_append_str( utf8stringbuf_t *this_, const char *appendix ) {
274 13951 : utf8error_t result = UTF8ERROR_SUCCESS;
275 13951 : if ( appendix == NULL ) {
276 1 : result = UTF8ERROR_NULL_PARAM;
277 : }
278 : else {
279 13950 : const size_t start = strlen( (*this_).buf );
280 :
281 13950 : const size_t appLen = strlen( appendix );
282 13950 : if ( start + appLen < (*this_).size ) {
283 : /* gcc does not reliably evaluate the if condition above */
284 : #if __GNUC__ >= 8
285 : #pragma GCC diagnostic push
286 : #pragma GCC diagnostic ignored "-Wstringop-overflow"
287 : #endif
288 13936 : memcpy( &((*this_).buf[start]), appendix, appLen+1 );
289 : #if __GNUC__ >= 8
290 : #pragma GCC diagnostic pop
291 : #endif
292 : }
293 : else {
294 14 : const size_t appPartLen = ((*this_).size-start)-1; /* cannot be negative */
295 14 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
296 : {
297 13 : memcpy( &((*this_).buf[start]), appendix, appPartLen );
298 : }
299 : else
300 : {
301 : /* buffer full */
302 : }
303 14 : utf8_string_buf_private_make_null_termination( this_ );
304 14 : result = UTF8ERROR_TRUNCATED;
305 : }
306 :
307 : /* For the standard use case, strlen and memcpy are faster than strncpy; strncat does not provide an error */
308 : }
309 13951 : return result;
310 : }
311 :
312 3 : static inline utf8error_t utf8stringbuf_append_buf( utf8stringbuf_t *this_, const utf8stringbuf_t *appendix ) {
313 3 : utf8error_t result = UTF8ERROR_SUCCESS;
314 3 : const size_t start = strlen( (*this_).buf );
315 :
316 3 : const size_t appLen = strlen( (*appendix).buf );
317 3 : if ( start + appLen < (*this_).size ) {
318 1 : memcpy( &((*this_).buf[start]), (*appendix).buf, appLen+1 );
319 : }
320 : else {
321 2 : const size_t appPartLen = ((*this_).size-start)-1;
322 2 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
323 : {
324 1 : memcpy( &((*this_).buf[start]), (*appendix).buf, appPartLen );
325 : }
326 : else
327 : {
328 : /* buffer full */
329 : }
330 2 : utf8_string_buf_private_make_null_termination( this_ );
331 2 : result = UTF8ERROR_TRUNCATED;
332 : }
333 :
334 : /* For the standard use case, strlen and memcpy are faster than strncpy; strncat does not provide an error */
335 3 : return result;
336 : }
337 :
338 3383 : static inline utf8error_t utf8stringbuf_append_int( utf8stringbuf_t *this_, const int64_t appendix ) {
339 : char numberStr[21]; /* this is sufficient for signed 64 bit integers: -9223372036854775806 */
340 : /* Note: snprintf is not available on every OS */
341 3383 : sprintf( numberStr, utf8stringbuf_private_format_signed_64_bit_int, appendix );
342 3383 : return utf8stringbuf_append_str( this_, numberStr );
343 : }
344 :
345 3 : static inline utf8error_t utf8stringbuf_append_hex( utf8stringbuf_t *this_, const uint64_t appendix ) {
346 : char numberStr[17]; /* this is sufficient for 64 bit integers */
347 : /* Note: snprintf is not available on every OS */
348 3 : sprintf( numberStr, utf8stringbuf_private_format_64_bit_hex, appendix );
349 3 : return utf8stringbuf_append_str( this_, numberStr );
350 : }
351 :
352 61 : static inline utf8stringbuf_t utf8stringbuf_get_end( utf8stringbuf_t *this_ ) {
353 61 : unsigned int this_Length = utf8stringbuf_get_length( this_ );
354 61 : return utf8stringbuf_new( &((*this_).buf[this_Length]), (*this_).size - this_Length );
355 : }
356 :
357 9 : static inline utf8error_t utf8stringbuf_append_view( utf8stringbuf_t *this_, const utf8stringview_t *appendix )
358 : {
359 9 : assert( appendix != NULL );
360 9 : utf8error_t result = UTF8ERROR_SUCCESS;
361 9 : const size_t start = strlen( (*this_).buf );
362 :
363 9 : const size_t appLen = utf8stringview_get_length( appendix );
364 9 : if ( start + appLen < (*this_).size ) {
365 7 : memcpy( &((*this_).buf[start]), utf8stringview_get_start( appendix ), appLen );
366 7 : (*this_).buf[start+appLen] = '\0';
367 : }
368 : else {
369 2 : const size_t appPartLen = ((*this_).size-start)-1;
370 2 : if (( appPartLen > 0 )&&( appPartLen <= PTRDIFF_MAX )) /* check to suppress compiler warning */
371 : {
372 1 : memcpy( &((*this_).buf[start]), utf8stringview_get_start( appendix ), appPartLen );
373 : }
374 : else
375 : {
376 : /* buffer full */
377 : }
378 2 : utf8_string_buf_private_make_null_termination( this_ );
379 2 : result = UTF8ERROR_TRUNCATED;
380 : }
381 :
382 9 : return result;
383 : }
384 :
385 : #ifdef __cplusplus
386 : }
387 : #endif
388 :
389 :
390 : /*
391 : * Copyright 2012-2025 Andreas Warnke
392 : *
393 : * Licensed under the Apache License, Version 2.0 (the "License");
394 : * you may not use this file except in compliance with the License.
395 : * You may obtain a copy of the License at
396 : *
397 : * http://www.apache.org/licenses/LICENSE-2.0
398 : *
399 : * Unless required by applicable law or agreed to in writing, software
400 : * distributed under the License is distributed on an "AS IS" BASIS,
401 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
402 : * See the License for the specific language governing permissions and
403 : * limitations under the License.
404 : */
|