blob: 0d2c0655c5031a4289bd2ae79089370e0d84cf53 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
Damjan Marionf1213b82016-03-13 02:22:06 +01002 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
Ed Warnickecb9cada2015-12-08 15:45:58 -070016 Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
Dave Barachb7b92992018-10-17 10:38:51 -040038/** \file
39
40 Optimized string handling code, including c11-compliant
41 "safe C library" variants.
42*/
43
Ed Warnickecb9cada2015-12-08 15:45:58 -070044#ifndef included_clib_string_h
45#define included_clib_string_h
46
Dave Barachc3799992016-08-15 11:12:27 -040047#include <vppinfra/clib.h> /* for CLIB_LINUX_KERNEL */
Damjan Marione319de02016-10-21 19:30:42 +020048#include <vppinfra/vector.h>
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010049#include <vppinfra/error_bootstrap.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070050
51#ifdef CLIB_LINUX_KERNEL
52#include <linux/string.h>
53#endif
54
55#ifdef CLIB_UNIX
56#include <string.h>
57#endif
58
59#ifdef CLIB_STANDALONE
60#include <vppinfra/standalone_string.h>
61#endif
62
Damjan Marionb2e1fe92017-11-22 12:41:32 +010063#if _x86_64_
64#include <x86intrin.h>
65#endif
66
Ed Warnickecb9cada2015-12-08 15:45:58 -070067/* Exchanges source and destination. */
Dave Barachc3799992016-08-15 11:12:27 -040068void clib_memswap (void *_a, void *_b, uword bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -070069
Dave Barachd4048a42016-11-07 09:55:55 -050070/*
71 * the vector unit memcpy variants confuse coverity
72 * so don't let it anywhere near them.
73 */
74#ifndef __COVERITY__
Damjan Marion162330f2020-04-29 21:28:15 +020075#if __AVX512BITALG__
Damjan Marionfad3fb32017-12-14 09:30:11 +010076#include <vppinfra/memcpy_avx512.h>
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010077#define clib_memcpy_fast_arch(a, b, c) clib_memcpy_fast_avx512 (a, b, c)
Damjan Marionfad3fb32017-12-14 09:30:11 +010078#elif __AVX2__
79#include <vppinfra/memcpy_avx2.h>
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010080#define clib_memcpy_fast_arch(a, b, c) clib_memcpy_fast_avx2 (a, b, c)
Damjan Marion793b18d2016-05-16 16:52:55 +020081#elif __SSSE3__
Damjan Marionf1213b82016-03-13 02:22:06 +010082#include <vppinfra/memcpy_sse3.h>
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010083#define clib_memcpy_fast_arch(a, b, c) clib_memcpy_fast_sse3 (a, b, c)
84#endif /* __AVX512BITALG__ */
85#endif /* __COVERITY__ */
86
87#ifndef clib_memcpy_fast_arch
88#define clib_memcpy_fast_arch(a, b, c) memcpy (a, b, c)
89#endif /* clib_memcpy_fast_arch */
90
91static_always_inline void *
92clib_memcpy_fast (void *restrict dst, const void *restrict src, size_t n)
93{
94 ASSERT (dst && src &&
95 "memcpy(src, dst, n) with src == NULL or dst == NULL is undefined "
96 "behaviour");
97 return clib_memcpy_fast_arch (dst, src, n);
98}
99
100#undef clib_memcpy_fast_arch
Damjan Marionf1213b82016-03-13 02:22:06 +0100101
Damjan Marion856d0622021-04-21 21:11:35 +0200102#include <vppinfra/memcpy.h>
103
Dave Barachb7b92992018-10-17 10:38:51 -0400104/* c-11 string manipulation variants */
105
106#ifndef EOK
107#define EOK 0
108#endif
109#ifndef EINVAL
110#define EINVAL 22
111#endif
Stevenb0598492018-10-24 21:15:45 -0700112#ifndef ESRCH
113#define ESRCH 3
114#endif
115#ifndef EOVERFLOW
116#define EOVERFLOW 75
117#endif
118
119/*
120 * In order to provide smooth mapping from unsafe string API to the clib string
121 * macro, we often have to improvise s1max and s2max due to the additional
122 * arguments are required for implementing the safe API. This macro is used
Stevenf09179f2019-01-07 20:32:01 -0800123 * to provide the s1max/s2max. It is not perfect because the actual
Stevenb0598492018-10-24 21:15:45 -0700124 * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
125 * the macro would cause a regression. However, it is not terribly likely.
126 * So I bet against the odds.
127 */
128#define CLIB_STRING_MACRO_MAX 4096
Dave Barachb7b92992018-10-17 10:38:51 -0400129
130typedef int errno_t;
131typedef uword rsize_t;
132
133void clib_c11_violation (const char *s);
134errno_t memcpy_s (void *__restrict__ dest, rsize_t dmax,
135 const void *__restrict__ src, rsize_t n);
136
137always_inline errno_t
138memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
139 const void *__restrict__ src, rsize_t n)
140{
141 uword low, hi;
142 u8 bad;
143
144 /*
Dave Barach178cf492018-11-13 16:34:13 -0500145 * Optimize constant-number-of-bytes calls without asking
146 * "too many questions for someone from New Jersey"
147 */
Damjan Marionb14c49d2021-04-25 10:55:53 +0200148 if (COMPILE_TIME_CONST (n))
Dave Barach178cf492018-11-13 16:34:13 -0500149 {
150 clib_memcpy_fast (dest, src, n);
151 return EOK;
152 }
153
154 /*
Dave Barachb7b92992018-10-17 10:38:51 -0400155 * call bogus if: src or dst NULL, trying to copy
156 * more data than we have space in dst, or src == dst.
157 * n == 0 isn't really "bad", so check first in the
158 * "wall-of-shame" department...
159 */
160 bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
161 if (PREDICT_FALSE (bad != 0))
162 {
163 /* Not actually trying to copy anything is OK */
164 if (n == 0)
165 return EOK;
166 if (dest == NULL)
167 clib_c11_violation ("dest NULL");
168 if (src == NULL)
169 clib_c11_violation ("src NULL");
170 if (n > dmax)
171 clib_c11_violation ("n > dmax");
172 if (dest == src)
173 clib_c11_violation ("dest == src");
174 return EINVAL;
175 }
176
177 /* Check for src/dst overlap, which is not allowed */
178 low = (uword) (src < dest ? src : dest);
179 hi = (uword) (src < dest ? dest : src);
180
181 if (PREDICT_FALSE (low + (n - 1) >= hi))
182 {
183 clib_c11_violation ("src/dest overlap");
184 return EINVAL;
185 }
186
Dave Barach178cf492018-11-13 16:34:13 -0500187 clib_memcpy_fast (dest, src, n);
Dave Barachb7b92992018-10-17 10:38:51 -0400188 return EOK;
189}
190
191/*
192 * Note: $$$ This macro is a crutch. Folks need to manually
193 * inspect every extant clib_memcpy(...) call and
194 * attempt to provide a real destination buffer size
195 * argument...
196 */
197#define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
198
199errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
200
201always_inline errno_t
202memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
203{
204 u8 bad;
205
206 bad = (s == 0) + (n > smax);
207
208 if (PREDICT_FALSE (bad != 0))
209 {
210 if (s == 0)
211 clib_c11_violation ("s NULL");
212 if (n > smax)
213 clib_c11_violation ("n > smax");
214 return (EINVAL);
215 }
216 memset (s, c, n);
217 return (EOK);
218}
219
220/*
221 * This macro is not [so much of] a crutch.
222 * It's super-typical to write:
223 *
224 * ep = pool_get (<pool>);
225 * clib_memset(ep, 0, sizeof (*ep));
226 *
227 * The compiler should delete the not-so useful
228 * (n > smax) test. TBH the NULL pointer check isn't
229 * so useful in this case, but so be it.
230 */
231#define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
232
Damjan Marion14864772018-05-22 14:07:47 +0200233static_always_inline void
Damjan Marionc59b9a22019-03-19 15:38:40 +0100234clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
235{
Igor Mikhailov (imichail)419e15f2019-05-13 12:04:04 -0700236#if defined (CLIB_HAVE_VEC256)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100237 u8x32 s0, s1, d0, d1;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100238 u8x32 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
239 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
240 };
241 u8x32 lv = u8x32_splat (len);
242 u8x32 add = u8x32_splat (32);
243
Damjan Marion4d3aa072019-03-28 16:19:24 +0100244 s0 = u8x32_load_unaligned (src);
245 s1 = u8x32_load_unaligned (src + 32);
246 d0 = u8x32_load_unaligned (dst);
247 d1 = u8x32_load_unaligned (dst + 32);
248
249 d0 = u8x32_blend (d0, s0, u8x32_is_greater (lv, mask));
250 u8x32_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100251
252 if (max_len <= 32)
253 return;
254
255 mask += add;
Damjan Marion4d3aa072019-03-28 16:19:24 +0100256 d1 = u8x32_blend (d1, s1, u8x32_is_greater (lv, mask));
257 u8x32_store_unaligned (d1, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100258
Lijian.Zhang37c83782019-04-04 15:26:26 +0800259#elif defined (CLIB_HAVE_VEC128)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100260 u8x16 s0, s1, s2, s3, d0, d1, d2, d3;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100261 u8x16 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
262 u8x16 lv = u8x16_splat (len);
263 u8x16 add = u8x16_splat (16);
264
Damjan Marion4d3aa072019-03-28 16:19:24 +0100265 s0 = u8x16_load_unaligned (src);
266 s1 = u8x16_load_unaligned (src + 16);
267 s2 = u8x16_load_unaligned (src + 32);
268 s3 = u8x16_load_unaligned (src + 48);
269 d0 = u8x16_load_unaligned (dst);
270 d1 = u8x16_load_unaligned (dst + 16);
271 d2 = u8x16_load_unaligned (dst + 32);
272 d3 = u8x16_load_unaligned (dst + 48);
273
274 d0 = u8x16_blend (d0, s0, u8x16_is_greater (lv, mask));
275 u8x16_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100276
277 if (max_len <= 16)
278 return;
279
280 mask += add;
Damjan Marion4d3aa072019-03-28 16:19:24 +0100281 d1 = u8x16_blend (d1, s1, u8x16_is_greater (lv, mask));
282 u8x16_store_unaligned (d1, dst + 16);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100283
284 if (max_len <= 32)
285 return;
286
287 mask += add;
Damjan Marion4d3aa072019-03-28 16:19:24 +0100288 d2 = u8x16_blend (d2, s2, u8x16_is_greater (lv, mask));
289 u8x16_store_unaligned (d2, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100290
291 mask += add;
Damjan Marion4d3aa072019-03-28 16:19:24 +0100292 d3 = u8x16_blend (d3, s3, u8x16_is_greater (lv, mask));
293 u8x16_store_unaligned (d3, dst + 48);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100294#else
Damjan Marion4d3aa072019-03-28 16:19:24 +0100295 memmove (dst, src, len);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100296#endif
297}
298
299static_always_inline void
300clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
301{
302 clib_memcpy_le (dst, src, len, 64);
303}
304
305static_always_inline void
306clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
307{
308 clib_memcpy_le (dst, src, len, 32);
309}
310
311static_always_inline void
Damjan Marion14864772018-05-22 14:07:47 +0200312clib_memset_u64 (void *p, u64 val, uword count)
313{
314 u64 *ptr = p;
315#if defined(CLIB_HAVE_VEC512)
316 u64x8 v512 = u64x8_splat (val);
317 while (count >= 8)
318 {
319 u64x8_store_unaligned (v512, ptr);
320 ptr += 8;
321 count -= 8;
322 }
323 if (count == 0)
324 return;
325#endif
326#if defined(CLIB_HAVE_VEC256)
327 u64x4 v256 = u64x4_splat (val);
328 while (count >= 4)
329 {
330 u64x4_store_unaligned (v256, ptr);
331 ptr += 4;
332 count -= 4;
333 }
334 if (count == 0)
335 return;
336#else
337 while (count >= 4)
338 {
339 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
340 ptr += 4;
341 count -= 4;
342 }
343#endif
344 while (count--)
345 ptr++[0] = val;
346}
347
348static_always_inline void
349clib_memset_u32 (void *p, u32 val, uword count)
350{
351 u32 *ptr = p;
352#if defined(CLIB_HAVE_VEC512)
353 u32x16 v512 = u32x16_splat (val);
354 while (count >= 16)
355 {
356 u32x16_store_unaligned (v512, ptr);
357 ptr += 16;
358 count -= 16;
359 }
360 if (count == 0)
361 return;
362#endif
363#if defined(CLIB_HAVE_VEC256)
364 u32x8 v256 = u32x8_splat (val);
365 while (count >= 8)
366 {
367 u32x8_store_unaligned (v256, ptr);
368 ptr += 8;
369 count -= 8;
370 }
371 if (count == 0)
372 return;
373#endif
374#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
375 u32x4 v128 = u32x4_splat (val);
376 while (count >= 4)
377 {
378 u32x4_store_unaligned (v128, ptr);
379 ptr += 4;
380 count -= 4;
381 }
382#else
383 while (count >= 4)
384 {
385 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
386 ptr += 4;
387 count -= 4;
388 }
389#endif
390 while (count--)
391 ptr++[0] = val;
392}
393
394static_always_inline void
395clib_memset_u16 (void *p, u16 val, uword count)
396{
397 u16 *ptr = p;
398#if defined(CLIB_HAVE_VEC512)
399 u16x32 v512 = u16x32_splat (val);
400 while (count >= 32)
401 {
402 u16x32_store_unaligned (v512, ptr);
403 ptr += 32;
404 count -= 32;
405 }
406 if (count == 0)
407 return;
408#endif
409#if defined(CLIB_HAVE_VEC256)
410 u16x16 v256 = u16x16_splat (val);
411 while (count >= 16)
412 {
413 u16x16_store_unaligned (v256, ptr);
414 ptr += 16;
415 count -= 16;
416 }
417 if (count == 0)
418 return;
419#endif
420#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
421 u16x8 v128 = u16x8_splat (val);
422 while (count >= 8)
423 {
424 u16x8_store_unaligned (v128, ptr);
425 ptr += 8;
426 count -= 8;
427 }
428#else
429 while (count >= 4)
430 {
431 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
432 ptr += 4;
433 count -= 4;
434 }
435#endif
436 while (count--)
437 ptr++[0] = val;
438}
439
440static_always_inline void
441clib_memset_u8 (void *p, u8 val, uword count)
442{
443 u8 *ptr = p;
444#if defined(CLIB_HAVE_VEC512)
445 u8x64 v512 = u8x64_splat (val);
446 while (count >= 64)
447 {
448 u8x64_store_unaligned (v512, ptr);
449 ptr += 64;
450 count -= 64;
451 }
452 if (count == 0)
453 return;
454#endif
455#if defined(CLIB_HAVE_VEC256)
456 u8x32 v256 = u8x32_splat (val);
457 while (count >= 32)
458 {
459 u8x32_store_unaligned (v256, ptr);
460 ptr += 32;
461 count -= 32;
462 }
463 if (count == 0)
464 return;
465#endif
466#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
467 u8x16 v128 = u8x16_splat (val);
468 while (count >= 16)
469 {
470 u8x16_store_unaligned (v128, ptr);
471 ptr += 16;
472 count -= 16;
473 }
474#else
475 while (count >= 4)
476 {
477 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
478 ptr += 4;
479 count -= 4;
480 }
481#endif
482 while (count--)
483 ptr++[0] = val;
484}
485
486static_always_inline uword
487clib_count_equal_u64 (u64 * data, uword max_count)
488{
Neale Ranns2329e092018-10-03 14:13:27 -0400489 uword count;
490 u64 first;
Damjan Marion14864772018-05-22 14:07:47 +0200491
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200492 if (max_count <= 1)
493 return max_count;
Damjan Marion008eef32018-09-12 22:37:30 +0200494 if (data[0] != data[1])
495 return 1;
496
Neale Ranns2329e092018-10-03 14:13:27 -0400497 count = 0;
498 first = data[0];
499
Damjan Marion008eef32018-09-12 22:37:30 +0200500#if defined(CLIB_HAVE_VEC256)
501 u64x4 splat = u64x4_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200502 while (count + 3 < max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200503 {
Damjan Marion008eef32018-09-12 22:37:30 +0200504 u64 bmp;
505 bmp = u8x32_msb_mask ((u8x32) (u64x4_load_unaligned (data) == splat));
506 if (bmp != 0xffffffff)
507 {
508 count += count_trailing_zeros (~bmp) / 8;
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200509 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200510 }
511
512 data += 4;
513 count += 4;
Damjan Marion14864772018-05-22 14:07:47 +0200514 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200515#else
Damjan Marion008eef32018-09-12 22:37:30 +0200516 count += 2;
517 data += 2;
Neale Ranns825fc482018-10-10 13:27:00 +0000518 while (count + 3 < max_count &&
Damjan Marion008eef32018-09-12 22:37:30 +0200519 ((data[0] ^ first) | (data[1] ^ first) |
520 (data[2] ^ first) | (data[3] ^ first)) == 0)
Damjan Marion14864772018-05-22 14:07:47 +0200521 {
522 data += 4;
523 count += 4;
Damjan Marion14864772018-05-22 14:07:47 +0200524 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200525#endif
Damjan Marion14864772018-05-22 14:07:47 +0200526 while (count < max_count && (data[0] == first))
527 {
528 data += 1;
529 count += 1;
530 }
531 return count;
532}
533
534static_always_inline uword
535clib_count_equal_u32 (u32 * data, uword max_count)
536{
Neale Ranns2329e092018-10-03 14:13:27 -0400537 uword count;
538 u32 first;
Damjan Marion14864772018-05-22 14:07:47 +0200539
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200540 if (max_count <= 1)
541 return max_count;
Damjan Marion008eef32018-09-12 22:37:30 +0200542 if (data[0] != data[1])
543 return 1;
544
Neale Ranns2329e092018-10-03 14:13:27 -0400545 count = 0;
546 first = data[0];
547
Damjan Marion14864772018-05-22 14:07:47 +0200548#if defined(CLIB_HAVE_VEC256)
Damjan Marion008eef32018-09-12 22:37:30 +0200549 u32x8 splat = u32x8_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200550 while (count + 7 < max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200551 {
Damjan Marion008eef32018-09-12 22:37:30 +0200552 u64 bmp;
553 bmp = u8x32_msb_mask ((u8x32) (u32x8_load_unaligned (data) == splat));
554 if (bmp != 0xffffffff)
555 {
556 count += count_trailing_zeros (~bmp) / 4;
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200557 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200558 }
559
Damjan Marion14864772018-05-22 14:07:47 +0200560 data += 8;
561 count += 8;
Damjan Marion008eef32018-09-12 22:37:30 +0200562 }
563#elif defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_MSB_MASK)
564 u32x4 splat = u32x4_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200565 while (count + 3 < max_count)
Damjan Marion008eef32018-09-12 22:37:30 +0200566 {
567 u64 bmp;
568 bmp = u8x16_msb_mask ((u8x16) (u32x4_load_unaligned (data) == splat));
569 if (bmp != 0xffff)
570 {
571 count += count_trailing_zeros (~bmp) / 4;
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200572 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200573 }
574
575 data += 4;
576 count += 4;
Damjan Marion14864772018-05-22 14:07:47 +0200577 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200578#else
Damjan Marion008eef32018-09-12 22:37:30 +0200579 count += 2;
580 data += 2;
Neale Ranns825fc482018-10-10 13:27:00 +0000581 while (count + 3 < max_count &&
Damjan Marion008eef32018-09-12 22:37:30 +0200582 ((data[0] ^ first) | (data[1] ^ first) |
583 (data[2] ^ first) | (data[3] ^ first)) == 0)
Damjan Marion14864772018-05-22 14:07:47 +0200584 {
585 data += 4;
586 count += 4;
Damjan Marion14864772018-05-22 14:07:47 +0200587 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200588#endif
Damjan Marion14864772018-05-22 14:07:47 +0200589 while (count < max_count && (data[0] == first))
590 {
591 data += 1;
592 count += 1;
593 }
594 return count;
595}
596
597static_always_inline uword
598clib_count_equal_u16 (u16 * data, uword max_count)
599{
Neale Ranns2329e092018-10-03 14:13:27 -0400600 uword count;
601 u16 first;
Damjan Marion14864772018-05-22 14:07:47 +0200602
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200603 if (max_count <= 1)
604 return max_count;
Damjan Marion008eef32018-09-12 22:37:30 +0200605 if (data[0] != data[1])
606 return 1;
607
Neale Ranns2329e092018-10-03 14:13:27 -0400608 count = 0;
609 first = data[0];
610
Damjan Marion14864772018-05-22 14:07:47 +0200611#if defined(CLIB_HAVE_VEC256)
Damjan Marion008eef32018-09-12 22:37:30 +0200612 u16x16 splat = u16x16_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200613 while (count + 15 < max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200614 {
Damjan Marion008eef32018-09-12 22:37:30 +0200615 u64 bmp;
616 bmp = u8x32_msb_mask ((u8x32) (u16x16_load_unaligned (data) == splat));
617 if (bmp != 0xffffffff)
618 {
619 count += count_trailing_zeros (~bmp) / 2;
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200620 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200621 }
622
Damjan Marion14864772018-05-22 14:07:47 +0200623 data += 16;
624 count += 16;
625 }
Damjan Marion008eef32018-09-12 22:37:30 +0200626#elif defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_MSB_MASK)
627 u16x8 splat = u16x8_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200628 while (count + 7 < max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200629 {
Damjan Marion008eef32018-09-12 22:37:30 +0200630 u64 bmp;
631 bmp = u8x16_msb_mask ((u8x16) (u16x8_load_unaligned (data) == splat));
632 if (bmp != 0xffff)
633 {
634 count += count_trailing_zeros (~bmp) / 2;
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200635 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200636 }
637
Damjan Marion14864772018-05-22 14:07:47 +0200638 data += 8;
639 count += 8;
640 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200641#else
Damjan Marion008eef32018-09-12 22:37:30 +0200642 count += 2;
643 data += 2;
Neale Ranns825fc482018-10-10 13:27:00 +0000644 while (count + 3 < max_count &&
Damjan Marion008eef32018-09-12 22:37:30 +0200645 ((data[0] ^ first) | (data[1] ^ first) |
646 (data[2] ^ first) | (data[3] ^ first)) == 0)
647 {
648 data += 4;
649 count += 4;
650 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200651#endif
Damjan Marion14864772018-05-22 14:07:47 +0200652 while (count < max_count && (data[0] == first))
653 {
654 data += 1;
655 count += 1;
656 }
657 return count;
658}
659
Damjan Marion008eef32018-09-12 22:37:30 +0200660static_always_inline uword
661clib_count_equal_u8 (u8 * data, uword max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200662{
Neale Ranns2329e092018-10-03 14:13:27 -0400663 uword count;
664 u8 first;
Damjan Marion14864772018-05-22 14:07:47 +0200665
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200666 if (max_count <= 1)
667 return max_count;
Damjan Marion008eef32018-09-12 22:37:30 +0200668 if (data[0] != data[1])
669 return 1;
670
Neale Ranns2329e092018-10-03 14:13:27 -0400671 count = 0;
672 first = data[0];
673
Damjan Marion14864772018-05-22 14:07:47 +0200674#if defined(CLIB_HAVE_VEC256)
Damjan Marion008eef32018-09-12 22:37:30 +0200675 u8x32 splat = u8x32_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200676 while (count + 31 < max_count)
Damjan Marion14864772018-05-22 14:07:47 +0200677 {
Damjan Marion008eef32018-09-12 22:37:30 +0200678 u64 bmp;
679 bmp = u8x32_msb_mask ((u8x32) (u8x32_load_unaligned (data) == splat));
680 if (bmp != 0xffffffff)
Damjan Marion1bc46cd2021-04-27 19:41:45 +0200681 return max_count;
Damjan Marion008eef32018-09-12 22:37:30 +0200682
Damjan Marion14864772018-05-22 14:07:47 +0200683 data += 32;
684 count += 32;
Damjan Marion008eef32018-09-12 22:37:30 +0200685 }
686#elif defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_MSB_MASK)
687 u8x16 splat = u8x16_splat (first);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200688 while (count + 15 < max_count)
Damjan Marion008eef32018-09-12 22:37:30 +0200689 {
690 u64 bmp;
691 bmp = u8x16_msb_mask ((u8x16) (u8x16_load_unaligned (data) == splat));
692 if (bmp != 0xffff)
693 {
694 count += count_trailing_zeros (~bmp);
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200695 return count;
Damjan Marion008eef32018-09-12 22:37:30 +0200696 }
697
698 data += 16;
699 count += 16;
Damjan Marion14864772018-05-22 14:07:47 +0200700 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200701#else
Damjan Marion008eef32018-09-12 22:37:30 +0200702 count += 2;
703 data += 2;
Neale Ranns825fc482018-10-10 13:27:00 +0000704 while (count + 3 < max_count &&
Damjan Marion008eef32018-09-12 22:37:30 +0200705 ((data[0] ^ first) | (data[1] ^ first) |
706 (data[2] ^ first) | (data[3] ^ first)) == 0)
Damjan Marion14864772018-05-22 14:07:47 +0200707 {
708 data += 4;
709 count += 4;
710 }
Benoît Ganne2a0bd4a2020-07-17 11:46:45 +0200711#endif
Damjan Marion14864772018-05-22 14:07:47 +0200712 while (count < max_count && (data[0] == first))
713 {
714 data += 1;
715 count += 1;
716 }
717 return count;
718}
719
Stevenb0598492018-10-24 21:15:45 -0700720/*
721 * This macro is to provide smooth mapping from memcmp to memcmp_s.
722 * memcmp has fewer parameters and fewer returns than memcmp_s.
723 * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
724 * we return 0 and spit out a message in the console because there is
725 * no way to return the error code to the memcmp callers.
726 * This condition happens when s1 or s2 is null. Please note
727 * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
728 * anyway. So we are consistent in this case for the comparison return
729 * although we also spit out a C11 violation message in the console to
730 * warn that they pass null pointers for both s1 and s2.
731 * Applications are encouraged to use the cool C11 memcmp_s API to get the
732 * maximum benefit out of it.
733 */
734#define clib_memcmp(s1,s2,m1) \
735 ({ int __diff = 0; \
736 memcmp_s_inline (s1, m1, s2, m1, &__diff); \
737 __diff; \
738 })
739
740errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
741 rsize_t s2max, int *diff);
742
743always_inline errno_t
744memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
745 int *diff)
746{
747 u8 bad;
748
749 bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
750 (s1max == 0);
751
752 if (PREDICT_FALSE (bad != 0))
753 {
754 if (s1 == NULL)
755 clib_c11_violation ("s1 NULL");
756 if (s2 == NULL)
757 clib_c11_violation ("s2 NULL");
758 if (diff == NULL)
759 clib_c11_violation ("diff NULL");
760 if (s2max > s1max)
761 clib_c11_violation ("s2max > s1max");
762 if (s2max == 0)
763 clib_c11_violation ("s2max 0");
764 if (s1max == 0)
765 clib_c11_violation ("s1max 0");
766 return EINVAL;
767 }
768
769 if (PREDICT_FALSE (s1 == s2))
770 {
771 *diff = 0;
772 return EOK;
773 }
774
775 *diff = memcmp (s1, s2, s2max);
776 return EOK;
777}
778
779/*
780 * This macro is to provide smooth mapping from strnlen to strnlen_s
781 */
782#define clib_strnlen(s,m) strnlen_s_inline(s,m)
783
784size_t strnlen_s (const char *s, size_t maxsize);
785
786always_inline size_t
787strnlen_s_inline (const char *s, size_t maxsize)
788{
789 u8 bad;
790
791 bad = (s == 0) + (maxsize == 0);
792 if (PREDICT_FALSE (bad != 0))
793 {
794 if (s == 0)
795 clib_c11_violation ("s NULL");
796 if (maxsize == 0)
797 clib_c11_violation ("maxsize 0");
798 return 0;
799 }
800 return strnlen (s, maxsize);
801}
802
803/*
804 * This macro is to provide smooth mapping from strcmp to strcmp_s.
805 * strcmp has fewer parameters and fewer returns than strcmp_s.
806 * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
807 * we return 0 and spit out a message in the console because
808 * there is no way to return the error to the strcmp callers.
809 * This condition happens when s1 or s2 is null. Please note in the extant
810 * strcmp call, they would end up crashing if one of them is null.
811 * So the new behavior is no crash, but an error is displayed in the
812 * console which I think is more user friendly. If both s1 and s2 are null,
813 * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
814 * to actually accessing the pointer contents. We are still consistent
815 * in this case for the comparison return although we also spit out a
816 * C11 violation message in the console to warn that they pass null pointers
817 * for both s1 and s2. The other problem is strcmp does not provide s1max,
818 * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
819 * If not, we may be accessing memory beyonf what is intended.
820 * Applications are encouraged to use the cool C11 strcmp_s API to get the
821 * maximum benefit out of it.
822 */
823#define clib_strcmp(s1,s2) \
824 ({ int __indicator = 0; \
825 strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
826 __indicator; \
827 })
828
829errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
830 int *indicator);
831
832always_inline errno_t
833strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
834 int *indicator)
835{
836 u8 bad;
837
838 bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
839 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
840
841 if (PREDICT_FALSE (bad != 0))
842 {
843 if (indicator == NULL)
844 clib_c11_violation ("indicator NULL");
845 if (s1 == NULL)
846 clib_c11_violation ("s1 NULL");
847 if (s2 == NULL)
848 clib_c11_violation ("s2 NULL");
849 if (s1max == 0)
850 clib_c11_violation ("s1max 0");
851 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
852 clib_c11_violation ("s1 unterminated");
853 return EINVAL;
854 }
855
856 *indicator = strcmp (s1, s2);
857 return EOK;
858}
859
860/*
861 * This macro is to provide smooth mapping from strncmp to strncmp_s.
862 * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
863 * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
864 * we return 0 and spit out a message in the console because there is no
865 * means to return the error to the strncmp caller.
866 * This condition happens when s1 or s2 is null. In the extant strncmp call,
867 * they would end up crashing if one of them is null. So the new behavior is
868 * no crash, but error is displayed in the console which is more
869 * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
870 * strncmp did the pointers comparison prior to actually accessing the
871 * pointer contents. We are still consistent in this case for the comparison
872 * return although we also spit out a C11 violation message in the console to
873 * warn that they pass null pointers for both s1 and s2.
874 * Applications are encouraged to use the cool C11 strncmp_s API to get the
875 * maximum benefit out of it.
876 */
877#define clib_strncmp(s1,s2,n) \
878 ({ int __indicator = 0; \
879 strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
880 __indicator; \
881 })
882
883errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
884 int *indicator);
885
886always_inline errno_t
887strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
888 int *indicator)
889{
890 u8 bad;
891 u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
892
893 if (PREDICT_FALSE (s1_greater_s1max && indicator))
894 {
895 /*
896 * strcmp allows n > s1max. If indicator is non null, we can still
897 * do the compare without any harm and return EINVAL as well as the
898 * result in indicator.
899 */
900 clib_c11_violation ("n exceeds s1 length");
901 *indicator = strncmp (s1, s2, n);
902 return EINVAL;
903 }
904
905 bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
906 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
907
908 if (PREDICT_FALSE (bad != 0))
909 {
910 if (indicator == NULL)
911 clib_c11_violation ("indicator NULL");
912 if (s1 == NULL)
913 clib_c11_violation ("s1 NULL");
914 if (s2 == NULL)
915 clib_c11_violation ("s2 NULL");
916 if (s1max == 0)
917 clib_c11_violation ("s1max 0");
918 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
919 clib_c11_violation ("s1 unterminated");
920 if (s1_greater_s1max)
921 clib_c11_violation ("n exceeds s1 length");
922 return EINVAL;
923 }
924
925 *indicator = strncmp (s1, s2, n);
926 return EOK;
927}
928
929/*
930 * This macro is provided for smooth migration from strcpy. It is not perfect
931 * because we don't know the size of the destination buffer to pass to strcpy_s.
932 * We improvise dmax with CLIB_STRING_MACRO_MAX.
933 * Applications are encouraged to move to the C11 strcpy_s API.
934 */
935#define clib_strcpy(d,s) strcpy_s_inline(d,CLIB_STRING_MACRO_MAX,s)
936
937errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
938 const char *__restrict__ src);
939
940always_inline errno_t
941strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
942 const char *__restrict__ src)
943{
944 u8 bad;
945 uword low, hi;
946 size_t n;
947
948 bad = (dest == 0) + (dmax == 0) + (src == 0);
949 if (PREDICT_FALSE (bad != 0))
950 {
951 if (dest == 0)
952 clib_c11_violation ("dest NULL");
953 if (src == 0)
954 clib_c11_violation ("src NULL");
955 if (dmax == 0)
956 clib_c11_violation ("dmax 0");
957 return EINVAL;
958 }
959
960 n = clib_strnlen (src, dmax);
961 if (PREDICT_FALSE (n >= dmax))
962 {
963 clib_c11_violation ("not enough space for dest");
964 return (EINVAL);
965 }
966 /* Not actually trying to copy anything is OK */
967 if (PREDICT_FALSE (n == 0))
968 return EOK;
969
970 /* Check for src/dst overlap, which is not allowed */
971 low = (uword) (src < dest ? src : dest);
972 hi = (uword) (src < dest ? dest : src);
973
974 if (PREDICT_FALSE (low + (n - 1) >= hi))
975 {
976 clib_c11_violation ("src/dest overlap");
977 return EINVAL;
978 }
979
980 clib_memcpy_fast (dest, src, n);
981 dest[n] = '\0';
982 return EOK;
983}
984
985/*
986 * This macro is provided for smooth migration from strncpy. It is not perfect
987 * because we don't know the size of the destination buffer to pass to
988 * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
989 * Applications are encouraged to move to the C11 strncpy_s API and provide
990 * the correct dmax for better error checking.
991 */
992#define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
993
994errno_t
995strncpy_s (char *__restrict__ dest, rsize_t dmax,
996 const char *__restrict__ src, rsize_t n);
997
998always_inline errno_t
999strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
1000 const char *__restrict__ src, rsize_t n)
1001{
1002 u8 bad;
1003 uword low, hi;
1004 rsize_t m;
1005 errno_t status = EOK;
1006
1007 bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
1008 if (PREDICT_FALSE (bad != 0))
1009 {
1010 /* Not actually trying to copy anything is OK */
1011 if (n == 0)
1012 return EOK;
1013 if (dest == 0)
1014 clib_c11_violation ("dest NULL");
1015 if (src == 0)
1016 clib_c11_violation ("src NULL");
1017 if (dmax == 0)
1018 clib_c11_violation ("dmax 0");
1019 return EINVAL;
1020 }
1021
1022 if (PREDICT_FALSE (n >= dmax))
1023 {
1024 /* Relax and use strnlen of src */
1025 clib_c11_violation ("n >= dmax");
1026 m = clib_strnlen (src, dmax);
1027 if (m >= dmax)
1028 {
1029 /* Truncate, adjust copy length to fit dest */
1030 m = dmax - 1;
1031 status = EOVERFLOW;
1032 }
1033 }
1034 else
Stevenf09179f2019-01-07 20:32:01 -08001035 /* cap the copy to strlen(src) in case n > strlen(src) */
1036 m = clib_strnlen (src, n);
Stevenb0598492018-10-24 21:15:45 -07001037
1038 /* Check for src/dst overlap, which is not allowed */
1039 low = (uword) (src < dest ? src : dest);
1040 hi = (uword) (src < dest ? dest : src);
1041
Dave Barachd08ae852018-12-05 08:41:11 -05001042 /*
1043 * This check may fail innocently if src + dmax >= dst, but
1044 * src + strlen(src) < dst. If it fails, check more carefully before
1045 * blowing the whistle.
1046 */
Stevenb0598492018-10-24 21:15:45 -07001047 if (PREDICT_FALSE (low + (m - 1) >= hi))
1048 {
Dave Barachd08ae852018-12-05 08:41:11 -05001049 m = clib_strnlen (src, m);
1050
1051 if (low + (m - 1) >= hi)
1052 {
1053 clib_c11_violation ("src/dest overlap");
1054 return EINVAL;
1055 }
Stevenb0598492018-10-24 21:15:45 -07001056 }
1057
1058 clib_memcpy_fast (dest, src, m);
1059 dest[m] = '\0';
1060 return status;
1061}
1062
1063/*
1064 * This macro is to provide smooth migration from strcat to strcat_s.
1065 * Because there is no dmax in strcat, we improvise it with
1066 * CLIB_STRING_MACRO_MAX. Please note there may be a chance to overwrite dest
1067 * with too many bytes from src.
1068 * Applications are encouraged to use C11 API to provide the actual dmax
1069 * for proper checking and protection.
1070 */
1071#define clib_strcat(d,s) strcat_s_inline(d,CLIB_STRING_MACRO_MAX,s)
1072
1073errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
1074 const char *__restrict__ src);
1075
1076always_inline errno_t
1077strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
1078 const char *__restrict__ src)
1079{
1080 u8 bad;
1081 uword low, hi;
1082 size_t m, n, dest_size;
1083
1084 bad = (dest == 0) + (dmax == 0) + (src == 0);
1085 if (PREDICT_FALSE (bad != 0))
1086 {
1087 if (dest == 0)
1088 clib_c11_violation ("dest NULL");
1089 if (src == 0)
1090 clib_c11_violation ("src NULL");
1091 if (dmax == 0)
1092 clib_c11_violation ("dmax 0");
1093 return EINVAL;
1094 }
1095
1096 dest_size = clib_strnlen (dest, dmax);
1097 m = dmax - dest_size;
1098 n = clib_strnlen (src, m);
1099 if (PREDICT_FALSE (n >= m))
1100 {
1101 clib_c11_violation ("not enough space for dest");
1102 return EINVAL;
1103 }
1104
1105 /* Not actually trying to concatenate anything is OK */
1106 if (PREDICT_FALSE (n == 0))
1107 return EOK;
1108
1109 /* Check for src/dst overlap, which is not allowed */
1110 low = (uword) (src < dest ? src : dest);
1111 hi = (uword) (src < dest ? dest : src);
1112
1113 if (PREDICT_FALSE (low + (n - 1) >= hi))
1114 {
1115 clib_c11_violation ("src/dest overlap");
1116 return EINVAL;
1117 }
1118
1119 clib_memcpy_fast (dest + dest_size, src, n);
1120 dest[dest_size + n] = '\0';
1121 return EOK;
1122}
1123
1124/*
1125 * This macro is to provide smooth migration from strncat to strncat_s.
1126 * The unsafe strncat does not have s1max. We improvise it with
1127 * CLIB_STRING_MACRO_MAX. Please note there may be a chance to overwrite
1128 * dest with too many bytes from src.
1129 * Applications are encouraged to move to C11 strncat_s which requires dmax
1130 * from the caller and provides checking to safeguard the memory corruption.
1131 */
1132#define clib_strncat(d,s,n) strncat_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
1133
1134errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
1135 const char *__restrict__ src, rsize_t n);
1136
1137always_inline errno_t
1138strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
1139 const char *__restrict__ src, rsize_t n)
1140{
1141 u8 bad;
1142 uword low, hi;
1143 size_t m, dest_size, allowed_size;
1144 errno_t status = EOK;
1145
1146 bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
1147 if (PREDICT_FALSE (bad != 0))
1148 {
1149 /* Not actually trying to concatenate anything is OK */
1150 if (n == 0)
1151 return EOK;
1152 if (dest == 0)
1153 clib_c11_violation ("dest NULL");
1154 if (src == 0)
1155 clib_c11_violation ("src NULL");
1156 if (dmax == 0)
1157 clib_c11_violation ("dmax 0");
1158 return EINVAL;
1159 }
1160
1161 /* Check for src/dst overlap, which is not allowed */
1162 low = (uword) (src < dest ? src : dest);
1163 hi = (uword) (src < dest ? dest : src);
1164
1165 if (PREDICT_FALSE (low + (n - 1) >= hi))
1166 {
1167 clib_c11_violation ("src/dest overlap");
1168 return EINVAL;
1169 }
1170
1171 dest_size = clib_strnlen (dest, dmax);
1172 allowed_size = dmax - dest_size;
1173
1174 if (PREDICT_FALSE (allowed_size == 0))
1175 {
1176 clib_c11_violation ("no space left in dest");
1177 return (EINVAL);
1178 }
1179
1180 if (PREDICT_FALSE (n >= allowed_size))
1181 {
1182 /*
1183 * unlike strcat_s, strncat_s will do the concatenation anyway when
1184 * there is not enough space in dest. But it will do the truncation and
1185 * null terminate dest
1186 */
1187 m = clib_strnlen (src, allowed_size);
1188 if (m >= allowed_size)
1189 {
1190 m = allowed_size - 1;
1191 status = EOVERFLOW;
1192 }
1193 }
1194 else
1195 m = clib_strnlen (src, n);
1196
1197 clib_memcpy_fast (dest + dest_size, src, m);
1198 dest[dest_size + m] = '\0';
1199 return status;
1200}
1201
1202/*
1203 * This macro is to provide smooth mapping from strtok_r to strtok_s.
1204 * To map strtok to this macro, the caller would have to supply an additional
1205 * argument. strtokr_s requires s1max which the unsafe API does not have. So
1206 * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
1207 * this macro cannot catch unterminated s1 and s2.
1208 * Applications are encouraged to use the cool C11 strtok_s API to avoid
1209 * these problems.
1210 */
1211#define clib_strtok(s1,s2,p) \
1212 ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
1213 strtok_s_inline (s1, &__s1max, s2, p); \
1214 })
1215
1216char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
1217 const char *__restrict__ s2, char **__restrict__ ptr);
1218
1219always_inline char *
1220strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
1221 const char *__restrict__ s2, char **__restrict__ ptr)
1222{
1223#define STRTOK_DELIM_MAX_LEN 16
1224 u8 bad;
1225 const char *pt;
1226 char *ptoken;
1227 uword dlen, slen;
1228
1229 bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
1230 ((s1 == 0) && ptr && (*ptr == 0));
1231 if (PREDICT_FALSE (bad != 0))
1232 {
1233 if (s2 == NULL)
1234 clib_c11_violation ("s2 NULL");
1235 if (s1max == NULL)
1236 clib_c11_violation ("s1max is NULL");
1237 if (ptr == NULL)
1238 clib_c11_violation ("ptr is NULL");
1239 /* s1 == 0 and *ptr == null is no good */
1240 if ((s1 == 0) && ptr && (*ptr == 0))
1241 clib_c11_violation ("s1 and ptr contents are NULL");
1242 return 0;
1243 }
1244
1245 if (s1 == 0)
1246 s1 = *ptr;
1247
1248 /*
1249 * scan s1 for a delimiter
1250 */
1251 dlen = *s1max;
1252 ptoken = 0;
1253 while (*s1 != '\0' && !ptoken)
1254 {
1255 if (PREDICT_FALSE (dlen == 0))
1256 {
1257 *ptr = 0;
1258 clib_c11_violation ("s1 unterminated");
1259 return 0;
1260 }
1261
1262 /*
1263 * must scan the entire delimiter list
1264 * ISO should have included a delimiter string limit!!
1265 */
1266 slen = STRTOK_DELIM_MAX_LEN;
1267 pt = s2;
1268 while (*pt != '\0')
1269 {
1270 if (PREDICT_FALSE (slen == 0))
1271 {
1272 *ptr = 0;
1273 clib_c11_violation ("s2 unterminated");
1274 return 0;
1275 }
1276 slen--;
1277 if (*s1 == *pt)
1278 {
1279 ptoken = 0;
1280 break;
1281 }
1282 else
1283 {
1284 pt++;
1285 ptoken = s1;
1286 }
1287 }
1288 s1++;
1289 dlen--;
1290 }
1291
1292 /*
1293 * if the beginning of a token was not found, then no
1294 * need to continue the scan.
1295 */
1296 if (ptoken == 0)
1297 {
1298 *s1max = dlen;
1299 return (ptoken);
1300 }
1301
1302 /*
1303 * Now we need to locate the end of the token
1304 */
1305 while (*s1 != '\0')
1306 {
1307 if (dlen == 0)
1308 {
1309 *ptr = 0;
1310 clib_c11_violation ("s1 unterminated");
1311 return 0;
1312 }
1313
1314 slen = STRTOK_DELIM_MAX_LEN;
1315 pt = s2;
1316 while (*pt != '\0')
1317 {
1318 if (slen == 0)
1319 {
1320 *ptr = 0;
1321 clib_c11_violation ("s2 unterminated");
1322 return 0;
1323 }
1324 slen--;
1325 if (*s1 == *pt)
1326 {
1327 /*
1328 * found a delimiter, set to null
1329 * and return context ptr to next char
1330 */
1331 *s1 = '\0';
1332 *ptr = (s1 + 1); /* return pointer for next scan */
1333 *s1max = dlen - 1; /* account for the nulled delimiter */
1334 return (ptoken);
1335 }
1336 else
1337 {
1338 /*
1339 * simply scanning through the delimiter string
1340 */
1341 pt++;
1342 }
1343 }
1344 s1++;
1345 dlen--;
1346 }
1347
1348 *ptr = s1;
1349 *s1max = dlen;
1350 return (ptoken);
1351}
1352
1353/*
1354 * This macro is to provide smooth mapping from strstr to strstr_s.
1355 * strstr_s requires s1max and s2max which the unsafe API does not have. So
1356 * we have to improvise them with CLIB_STRING_MACRO_MAX which may cause us
1357 * to access memory beyond it is intended if s1 or s2 is unterminated.
1358 * For the record, strstr crashes if s1 or s2 is unterminated. But this macro
1359 * does not.
1360 * Applications are encouraged to use the cool C11 strstr_s API to avoid
1361 * this problem.
1362 */
1363#define clib_strstr(s1,s2) \
1364 ({ char * __substring = 0; \
1365 strstr_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, CLIB_STRING_MACRO_MAX, \
1366 &__substring); \
1367 __substring; \
1368 })
1369
1370errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1371 char **substring);
1372
1373always_inline errno_t
1374strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1375 char **substring)
1376{
1377 u8 bad;
1378 size_t s1_size, s2_size;
1379
1380 bad =
1381 (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
1382 (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
1383 (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
1384 if (PREDICT_FALSE (bad != 0))
1385 {
1386 if (s1 == 0)
1387 clib_c11_violation ("s1 NULL");
1388 if (s2 == 0)
1389 clib_c11_violation ("s2 NULL");
1390 if (s1max == 0)
1391 clib_c11_violation ("s1max 0");
1392 if (s2max == 0)
1393 clib_c11_violation ("s2max 0");
1394 if (substring == 0)
1395 clib_c11_violation ("substring NULL");
1396 if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
1397 clib_c11_violation ("s1 unterminated");
1398 if (s2 && s2max && (s2[clib_strnlen (s2, s1max)] != '\0'))
1399 clib_c11_violation ("s2 unterminated");
1400 return EINVAL;
1401 }
1402
1403 /*
1404 * s2 points to a string with zero length, or s2 equals s1, return s1
1405 */
1406 if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1407 {
1408 *substring = s1;
1409 return EOK;
1410 }
1411
1412 /*
1413 * s2_size > s1_size, it won't find match.
1414 */
1415 s1_size = clib_strnlen (s1, s1max);
1416 s2_size = clib_strnlen (s2, s2max);
1417 if (PREDICT_FALSE (s2_size > s1_size))
1418 return ESRCH;
1419
1420 *substring = strstr (s1, s2);
1421 if (*substring == 0)
1422 return ESRCH;
1423
1424 return EOK;
1425}
1426
Ed Warnickecb9cada2015-12-08 15:45:58 -07001427#endif /* included_clib_string_h */
Dave Barachc3799992016-08-15 11:12:27 -04001428
1429/*
1430 * fd.io coding-style-patch-verification: ON
1431 *
1432 * Local Variables:
1433 * eval: (c-set-style "gnu")
1434 * End:
1435 */