2 * 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
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:
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
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.
40 Optimized string handling code, including c11-compliant
41 "safe C library" variants.
44 #ifndef included_clib_string_h
45 #define included_clib_string_h
47 #include <vppinfra/clib.h> /* for CLIB_LINUX_KERNEL */
48 #include <vppinfra/vector.h>
49 #include <vppinfra/error_bootstrap.h>
50 #include <vppinfra/memcpy_x86_64.h>
52 #ifdef CLIB_LINUX_KERNEL
53 #include <linux/string.h>
60 #ifdef CLIB_STANDALONE
61 #include <vppinfra/standalone_string.h>
65 #include <x86intrin.h>
68 /* Exchanges source and destination. */
69 void clib_memswap (void *_a, void *_b, uword bytes);
72 static_always_inline void *
73 clib_memcpy_fast (void *restrict dst, const void *restrict src, size_t n)
76 "memcpy(src, dst, n) with src == NULL or dst == NULL is undefined "
78 #if defined(__COVERITY__)
79 return memcpy (dst, src, n);
80 #elif defined(__x86_64__)
81 clib_memcpy_x86_64 (dst, src, n);
84 return memcpy (dst, src, n);
88 #include <vppinfra/memcpy.h>
90 /* c-11 string manipulation variants */
106 * In order to provide smooth mapping from unsafe string API to the clib string
107 * macro, we often have to improvise s1max and s2max due to the additional
108 * arguments are required for implementing the safe API. This macro is used
109 * to provide the s1max/s2max. It is not perfect because the actual
110 * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
111 * the macro would cause a regression. However, it is not terribly likely.
112 * So I bet against the odds.
114 #define CLIB_STRING_MACRO_MAX 4096
117 typedef uword rsize_t;
119 void clib_c11_violation (const char *s);
120 errno_t memcpy_s (void *__restrict__ dest, rsize_t dmax,
121 const void *__restrict__ src, rsize_t n);
123 always_inline errno_t
124 memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
125 const void *__restrict__ src, rsize_t n)
131 * Optimize constant-number-of-bytes calls without asking
132 * "too many questions for someone from New Jersey"
134 if (COMPILE_TIME_CONST (n))
136 clib_memcpy_fast (dest, src, n);
141 * call bogus if: src or dst NULL, trying to copy
142 * more data than we have space in dst, or src == dst.
143 * n == 0 isn't really "bad", so check first in the
144 * "wall-of-shame" department...
146 bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
147 if (PREDICT_FALSE (bad != 0))
149 /* Not actually trying to copy anything is OK */
153 clib_c11_violation ("dest NULL");
155 clib_c11_violation ("src NULL");
157 clib_c11_violation ("n > dmax");
159 clib_c11_violation ("dest == src");
163 /* Check for src/dst overlap, which is not allowed */
164 low = (uword) (src < dest ? src : dest);
165 hi = (uword) (src < dest ? dest : src);
167 if (PREDICT_FALSE (low + (n - 1) >= hi))
169 clib_c11_violation ("src/dest overlap");
173 clib_memcpy_fast (dest, src, n);
178 * Note: $$$ This macro is a crutch. Folks need to manually
179 * inspect every extant clib_memcpy(...) call and
180 * attempt to provide a real destination buffer size
183 #define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
185 errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
187 always_inline errno_t
188 memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
192 bad = (s == 0) + (n > smax);
194 if (PREDICT_FALSE (bad != 0))
197 clib_c11_violation ("s NULL");
199 clib_c11_violation ("n > smax");
207 * This macro is not [so much of] a crutch.
208 * It's super-typical to write:
210 * ep = pool_get (<pool>);
211 * clib_memset(ep, 0, sizeof (*ep));
213 * The compiler should delete the not-so useful
214 * (n > smax) test. TBH the NULL pointer check isn't
215 * so useful in this case, but so be it.
217 #define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
219 static_always_inline void
220 clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
222 #if defined (CLIB_HAVE_VEC256)
223 u8x32 s0, s1, d0, d1;
224 u8x32 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
225 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
227 u8x32 lv = u8x32_splat (len);
228 u8x32 add = u8x32_splat (32);
230 s0 = u8x32_load_unaligned (src);
231 s1 = u8x32_load_unaligned (src + 32);
232 d0 = u8x32_load_unaligned (dst);
233 d1 = u8x32_load_unaligned (dst + 32);
235 d0 = u8x32_blend (d0, s0, lv > mask);
236 u8x32_store_unaligned (d0, dst);
242 d1 = u8x32_blend (d1, s1, lv > mask);
243 u8x32_store_unaligned (d1, dst + 32);
245 #elif defined (CLIB_HAVE_VEC128)
246 u8x16 s0, s1, s2, s3, d0, d1, d2, d3;
247 u8x16 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
248 u8x16 lv = u8x16_splat (len);
249 u8x16 add = u8x16_splat (16);
251 s0 = u8x16_load_unaligned (src);
252 s1 = u8x16_load_unaligned (src + 16);
253 s2 = u8x16_load_unaligned (src + 32);
254 s3 = u8x16_load_unaligned (src + 48);
255 d0 = u8x16_load_unaligned (dst);
256 d1 = u8x16_load_unaligned (dst + 16);
257 d2 = u8x16_load_unaligned (dst + 32);
258 d3 = u8x16_load_unaligned (dst + 48);
260 d0 = u8x16_blend (d0, s0, lv > mask);
261 u8x16_store_unaligned (d0, dst);
267 d1 = u8x16_blend (d1, s1, lv > mask);
268 u8x16_store_unaligned (d1, dst + 16);
274 d2 = u8x16_blend (d2, s2, lv > mask);
275 u8x16_store_unaligned (d2, dst + 32);
278 d3 = u8x16_blend (d3, s3, lv > mask);
279 u8x16_store_unaligned (d3, dst + 48);
281 memmove (dst, src, len);
285 static_always_inline void
286 clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
288 clib_memcpy_le (dst, src, len, 64);
291 static_always_inline void
292 clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
294 clib_memcpy_le (dst, src, len, 32);
297 static_always_inline void
298 clib_memset_u64 (void *p, u64 val, uword count)
301 #if defined(CLIB_HAVE_VEC512)
302 u64x8 v512 = u64x8_splat (val);
305 u64x8_store_unaligned (v512, ptr);
312 #if defined(CLIB_HAVE_VEC256)
313 u64x4 v256 = u64x4_splat (val);
316 u64x4_store_unaligned (v256, ptr);
323 #if defined(CLIB_HAVE_VEC128)
324 u64x2 v = u64x2_splat (val);
328 #if defined(CLIB_HAVE_VEC128)
329 u64x2_store_unaligned (v, ptr);
330 u64x2_store_unaligned (v, ptr + 2);
332 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
342 static_always_inline void
343 clib_memset_u32 (void *p, u32 val, uword count)
346 #if defined(CLIB_HAVE_VEC512)
347 u32x16 v512 = u32x16_splat (val);
350 u32x16_store_unaligned (v512, ptr);
357 #if defined(CLIB_HAVE_VEC256)
358 u32x8 v256 = u32x8_splat (val);
361 u32x8_store_unaligned (v256, ptr);
368 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
369 u32x4 v128 = u32x4_splat (val);
372 u32x4_store_unaligned (v128, ptr);
379 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
388 static_always_inline void
389 clib_memset_u16 (void *p, u16 val, uword count)
392 #if defined(CLIB_HAVE_VEC512)
393 u16x32 v512 = u16x32_splat (val);
396 u16x32_store_unaligned (v512, ptr);
403 #if defined(CLIB_HAVE_VEC256)
404 u16x16 v256 = u16x16_splat (val);
407 u16x16_store_unaligned (v256, ptr);
414 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
415 u16x8 v128 = u16x8_splat (val);
418 u16x8_store_unaligned (v128, ptr);
425 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
434 static_always_inline void
435 clib_memset_u8 (void *p, u8 val, uword count)
438 #if defined(CLIB_HAVE_VEC512)
439 u8x64 v512 = u8x64_splat (val);
442 u8x64_store_unaligned (v512, ptr);
449 #if defined(CLIB_HAVE_VEC256)
450 u8x32 v256 = u8x32_splat (val);
453 u8x32_store_unaligned (v256, ptr);
460 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
461 u8x16 v128 = u8x16_splat (val);
464 u8x16_store_unaligned (v128, ptr);
471 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
482 * This macro is to provide smooth mapping from memcmp to memcmp_s.
483 * memcmp has fewer parameters and fewer returns than memcmp_s.
484 * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
485 * we return 0 and spit out a message in the console because there is
486 * no way to return the error code to the memcmp callers.
487 * This condition happens when s1 or s2 is null. Please note
488 * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
489 * anyway. So we are consistent in this case for the comparison return
490 * although we also spit out a C11 violation message in the console to
491 * warn that they pass null pointers for both s1 and s2.
492 * Applications are encouraged to use the cool C11 memcmp_s API to get the
493 * maximum benefit out of it.
495 #define clib_memcmp(s1,s2,m1) \
497 memcmp_s_inline (s1, m1, s2, m1, &__diff); \
501 errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
502 rsize_t s2max, int *diff);
504 always_inline errno_t
505 memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
510 bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
513 if (PREDICT_FALSE (bad != 0))
516 clib_c11_violation ("s1 NULL");
518 clib_c11_violation ("s2 NULL");
520 clib_c11_violation ("diff NULL");
522 clib_c11_violation ("s2max > s1max");
524 clib_c11_violation ("s2max 0");
526 clib_c11_violation ("s1max 0");
530 if (PREDICT_FALSE (s1 == s2))
536 *diff = memcmp (s1, s2, s2max);
541 * This macro is to provide smooth mapping from strnlen to strnlen_s
543 #define clib_strnlen(s,m) strnlen_s_inline(s,m)
545 size_t strnlen_s (const char *s, size_t maxsize);
548 strnlen_s_inline (const char *s, size_t maxsize)
552 bad = (s == 0) + (maxsize == 0);
553 if (PREDICT_FALSE (bad != 0))
556 clib_c11_violation ("s NULL");
558 clib_c11_violation ("maxsize 0");
561 return strnlen (s, maxsize);
565 * This macro is to provide smooth mapping from strcmp to strcmp_s.
566 * strcmp has fewer parameters and fewer returns than strcmp_s.
567 * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
568 * we return 0 and spit out a message in the console because
569 * there is no way to return the error to the strcmp callers.
570 * This condition happens when s1 or s2 is null. Please note in the extant
571 * strcmp call, they would end up crashing if one of them is null.
572 * So the new behavior is no crash, but an error is displayed in the
573 * console which I think is more user friendly. If both s1 and s2 are null,
574 * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
575 * to actually accessing the pointer contents. We are still consistent
576 * in this case for the comparison return although we also spit out a
577 * C11 violation message in the console to warn that they pass null pointers
578 * for both s1 and s2. The other problem is strcmp does not provide s1max,
579 * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
580 * If not, we may be accessing memory beyonf what is intended.
581 * Applications are encouraged to use the cool C11 strcmp_s API to get the
582 * maximum benefit out of it.
584 #define clib_strcmp(s1,s2) \
585 ({ int __indicator = 0; \
586 strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
590 errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
593 always_inline errno_t
594 strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
599 bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
600 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
602 if (PREDICT_FALSE (bad != 0))
604 if (indicator == NULL)
605 clib_c11_violation ("indicator NULL");
607 clib_c11_violation ("s1 NULL");
609 clib_c11_violation ("s2 NULL");
611 clib_c11_violation ("s1max 0");
612 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
613 clib_c11_violation ("s1 unterminated");
617 *indicator = strcmp (s1, s2);
622 * This macro is to provide smooth mapping from strncmp to strncmp_s.
623 * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
624 * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
625 * we return 0 and spit out a message in the console because there is no
626 * means to return the error to the strncmp caller.
627 * This condition happens when s1 or s2 is null. In the extant strncmp call,
628 * they would end up crashing if one of them is null. So the new behavior is
629 * no crash, but error is displayed in the console which is more
630 * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
631 * strncmp did the pointers comparison prior to actually accessing the
632 * pointer contents. We are still consistent in this case for the comparison
633 * return although we also spit out a C11 violation message in the console to
634 * warn that they pass null pointers for both s1 and s2.
635 * Applications are encouraged to use the cool C11 strncmp_s API to get the
636 * maximum benefit out of it.
638 #define clib_strncmp(s1,s2,n) \
639 ({ int __indicator = 0; \
640 strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
644 errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
647 always_inline errno_t
648 strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
652 u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
654 if (PREDICT_FALSE (s1_greater_s1max && indicator))
657 * strcmp allows n > s1max. If indicator is non null, we can still
658 * do the compare without any harm and return EINVAL as well as the
659 * result in indicator.
661 clib_c11_violation ("n exceeds s1 length");
662 *indicator = strncmp (s1, s2, n);
666 bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
667 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
669 if (PREDICT_FALSE (bad != 0))
671 if (indicator == NULL)
672 clib_c11_violation ("indicator NULL");
674 clib_c11_violation ("s1 NULL");
676 clib_c11_violation ("s2 NULL");
678 clib_c11_violation ("s1max 0");
679 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
680 clib_c11_violation ("s1 unterminated");
681 if (s1_greater_s1max)
682 clib_c11_violation ("n exceeds s1 length");
686 *indicator = strncmp (s1, s2, n);
690 errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
691 const char *__restrict__ src);
693 always_inline errno_t
694 strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
695 const char *__restrict__ src)
701 bad = (dest == 0) + (dmax == 0) + (src == 0);
702 if (PREDICT_FALSE (bad != 0))
705 clib_c11_violation ("dest NULL");
707 clib_c11_violation ("src NULL");
709 clib_c11_violation ("dmax 0");
713 n = clib_strnlen (src, dmax);
714 if (PREDICT_FALSE (n >= dmax))
716 clib_c11_violation ("not enough space for dest");
719 /* Not actually trying to copy anything is OK */
720 if (PREDICT_FALSE (n == 0))
723 /* Check for src/dst overlap, which is not allowed */
724 low = (uword) (src < dest ? src : dest);
725 hi = (uword) (src < dest ? dest : src);
727 if (PREDICT_FALSE (low + (n - 1) >= hi))
729 clib_c11_violation ("src/dest overlap");
733 clib_memcpy_fast (dest, src, n);
739 * This macro is provided for smooth migration from strncpy. It is not perfect
740 * because we don't know the size of the destination buffer to pass to
741 * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
742 * Applications are encouraged to move to the C11 strncpy_s API and provide
743 * the correct dmax for better error checking.
745 #define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
748 strncpy_s (char *__restrict__ dest, rsize_t dmax,
749 const char *__restrict__ src, rsize_t n);
751 always_inline errno_t
752 strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
753 const char *__restrict__ src, rsize_t n)
758 errno_t status = EOK;
760 bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
761 if (PREDICT_FALSE (bad != 0))
763 /* Not actually trying to copy anything is OK */
767 clib_c11_violation ("dest NULL");
769 clib_c11_violation ("src NULL");
771 clib_c11_violation ("dmax 0");
775 if (PREDICT_FALSE (n >= dmax))
777 /* Relax and use strnlen of src */
778 clib_c11_violation ("n >= dmax");
779 m = clib_strnlen (src, dmax);
782 /* Truncate, adjust copy length to fit dest */
788 /* cap the copy to strlen(src) in case n > strlen(src) */
789 m = clib_strnlen (src, n);
791 /* Check for src/dst overlap, which is not allowed */
792 low = (uword) (src < dest ? src : dest);
793 hi = (uword) (src < dest ? dest : src);
796 * This check may fail innocently if src + dmax >= dst, but
797 * src + strlen(src) < dst. If it fails, check more carefully before
798 * blowing the whistle.
800 if (PREDICT_FALSE (low + (m - 1) >= hi))
802 m = clib_strnlen (src, m);
804 if (low + (m - 1) >= hi)
806 clib_c11_violation ("src/dest overlap");
811 clib_memcpy_fast (dest, src, m);
816 errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
817 const char *__restrict__ src);
819 always_inline errno_t
820 strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
821 const char *__restrict__ src)
825 size_t m, n, dest_size;
827 bad = (dest == 0) + (dmax == 0) + (src == 0);
828 if (PREDICT_FALSE (bad != 0))
831 clib_c11_violation ("dest NULL");
833 clib_c11_violation ("src NULL");
835 clib_c11_violation ("dmax 0");
839 dest_size = clib_strnlen (dest, dmax);
840 m = dmax - dest_size;
841 n = clib_strnlen (src, m);
842 if (PREDICT_FALSE (n >= m))
844 clib_c11_violation ("not enough space for dest");
848 /* Not actually trying to concatenate anything is OK */
849 if (PREDICT_FALSE (n == 0))
852 /* Check for src/dst overlap, which is not allowed */
853 low = (uword) (src < dest ? src : dest);
854 hi = (uword) (src < dest ? dest : src);
856 if (PREDICT_FALSE (low + (n - 1) >= hi))
858 clib_c11_violation ("src/dest overlap");
862 clib_memcpy_fast (dest + dest_size, src, n);
863 dest[dest_size + n] = '\0';
867 errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
868 const char *__restrict__ src, rsize_t n);
870 always_inline errno_t
871 strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
872 const char *__restrict__ src, rsize_t n)
876 size_t m, dest_size, allowed_size;
877 errno_t status = EOK;
879 bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
880 if (PREDICT_FALSE (bad != 0))
882 /* Not actually trying to concatenate anything is OK */
886 clib_c11_violation ("dest NULL");
888 clib_c11_violation ("src NULL");
890 clib_c11_violation ("dmax 0");
894 /* Check for src/dst overlap, which is not allowed */
895 low = (uword) (src < dest ? src : dest);
896 hi = (uword) (src < dest ? dest : src);
898 if (PREDICT_FALSE (low + (n - 1) >= hi))
900 clib_c11_violation ("src/dest overlap");
904 dest_size = clib_strnlen (dest, dmax);
905 allowed_size = dmax - dest_size;
907 if (PREDICT_FALSE (allowed_size == 0))
909 clib_c11_violation ("no space left in dest");
913 if (PREDICT_FALSE (n >= allowed_size))
916 * unlike strcat_s, strncat_s will do the concatenation anyway when
917 * there is not enough space in dest. But it will do the truncation and
918 * null terminate dest
920 m = clib_strnlen (src, allowed_size);
921 if (m >= allowed_size)
923 m = allowed_size - 1;
928 m = clib_strnlen (src, n);
930 clib_memcpy_fast (dest + dest_size, src, m);
931 dest[dest_size + m] = '\0';
936 * This macro is to provide smooth mapping from strtok_r to strtok_s.
937 * To map strtok to this macro, the caller would have to supply an additional
938 * argument. strtokr_s requires s1max which the unsafe API does not have. So
939 * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
940 * this macro cannot catch unterminated s1 and s2.
941 * Applications are encouraged to use the cool C11 strtok_s API to avoid
944 #define clib_strtok(s1,s2,p) \
945 ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
946 strtok_s_inline (s1, &__s1max, s2, p); \
949 char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
950 const char *__restrict__ s2, char **__restrict__ ptr);
953 strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
954 const char *__restrict__ s2, char **__restrict__ ptr)
956 #define STRTOK_DELIM_MAX_LEN 16
962 bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
963 ((s1 == 0) && ptr && (*ptr == 0));
964 if (PREDICT_FALSE (bad != 0))
967 clib_c11_violation ("s2 NULL");
969 clib_c11_violation ("s1max is NULL");
971 clib_c11_violation ("ptr is NULL");
972 /* s1 == 0 and *ptr == null is no good */
973 if ((s1 == 0) && ptr && (*ptr == 0))
974 clib_c11_violation ("s1 and ptr contents are NULL");
982 * scan s1 for a delimiter
986 while (*s1 != '\0' && !ptoken)
988 if (PREDICT_FALSE (dlen == 0))
991 clib_c11_violation ("s1 unterminated");
996 * must scan the entire delimiter list
997 * ISO should have included a delimiter string limit!!
999 slen = STRTOK_DELIM_MAX_LEN;
1003 if (PREDICT_FALSE (slen == 0))
1006 clib_c11_violation ("s2 unterminated");
1026 * if the beginning of a token was not found, then no
1027 * need to continue the scan.
1036 * Now we need to locate the end of the token
1043 clib_c11_violation ("s1 unterminated");
1047 slen = STRTOK_DELIM_MAX_LEN;
1054 clib_c11_violation ("s2 unterminated");
1061 * found a delimiter, set to null
1062 * and return context ptr to next char
1065 *ptr = (s1 + 1); /* return pointer for next scan */
1066 *s1max = dlen - 1; /* account for the nulled delimiter */
1072 * simply scanning through the delimiter string
1086 errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1089 always_inline errno_t
1090 strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1094 size_t s1_size, s2_size;
1097 (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
1098 (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
1099 (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
1100 if (PREDICT_FALSE (bad != 0))
1103 clib_c11_violation ("s1 NULL");
1105 clib_c11_violation ("s2 NULL");
1107 clib_c11_violation ("s1max 0");
1109 clib_c11_violation ("s2max 0");
1111 clib_c11_violation ("substring NULL");
1112 if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
1113 clib_c11_violation ("s1 unterminated");
1114 if (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'))
1115 clib_c11_violation ("s2 unterminated");
1120 * s2 points to a string with zero length, or s2 equals s1, return s1
1122 if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1129 * s2_size > s1_size, it won't find match.
1131 s1_size = clib_strnlen (s1, s1max);
1132 s2_size = clib_strnlen (s2, s2max);
1133 if (PREDICT_FALSE (s2_size > s1_size))
1136 *substring = strstr (s1, s2);
1137 if (*substring == 0)
1143 #endif /* included_clib_string_h */
1146 * fd.io coding-style-patch-verification: ON
1149 * eval: (c-set-style "gnu")