LCOV - code coverage report
Current view: top level - vppinfra - string.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 374 445 84.0 %
Date: 2023-10-26 01:39:38 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /*
       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:
       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             : /*
      16             :   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             : 
      38             : /** \file
      39             : 
      40             :     Optimized string handling code, including c11-compliant
      41             :     "safe C library" variants.
      42             : */
      43             : 
      44             : #ifndef included_clib_string_h
      45             : #define included_clib_string_h
      46             : 
      47             : #include <vppinfra/clib.h>        /* for CLIB_LINUX_KERNEL */
      48             : #include <vppinfra/vector.h>
      49             : #include <vppinfra/error_bootstrap.h>
      50             : #ifdef __SSE4_2__
      51             : #include <vppinfra/memcpy_x86_64.h>
      52             : #endif
      53             : 
      54             : #ifdef CLIB_LINUX_KERNEL
      55             : #include <linux/string.h>
      56             : #endif
      57             : 
      58             : #ifdef CLIB_UNIX
      59             : #include <string.h>
      60             : #endif
      61             : 
      62             : #ifdef CLIB_STANDALONE
      63             : #include <vppinfra/standalone_string.h>
      64             : #endif
      65             : 
      66             : #if _x86_64_
      67             : #include <x86intrin.h>
      68             : #endif
      69             : 
      70             : /* Exchanges source and destination. */
      71             : void clib_memswap (void *_a, void *_b, uword bytes);
      72             : 
      73             : 
      74             : static_always_inline void *
      75  2340883103 : clib_memcpy_fast (void *restrict dst, const void *restrict src, size_t n)
      76             : {
      77  2340883103 :   ASSERT (dst && src &&
      78             :           "memcpy(src, dst, n) with src == NULL or dst == NULL is undefined "
      79             :           "behaviour");
      80             : #if defined(__COVERITY__)
      81             :   return memcpy (dst, src, n);
      82             : #elif defined(__SSE4_2__)
      83  2340883424 :   clib_memcpy_x86_64 (dst, src, n);
      84  2340947924 :   return dst;
      85             : #else
      86             :   return memcpy (dst, src, n);
      87             : #endif
      88             : }
      89             : 
      90             : static_always_inline void *
      91    61121974 : clib_memmove (void *dst, const void *src, size_t n)
      92             : {
      93    61121974 :   u8 *d = (u8 *) dst;
      94    61121974 :   u8 *s = (u8 *) src;
      95             : 
      96    61121974 :   if (s == d)
      97           0 :     return d;
      98             : 
      99    61121974 :   if (d > s)
     100  9787440102 :     for (uword i = n - 1; (i + 1) > 0; i--)
     101  9726498304 :       d[i] = s[i];
     102             :   else
     103   423192952 :     for (uword i = 0; i < n; i++)
     104   423002727 :       d[i] = s[i];
     105             : 
     106    61121974 :   return d;
     107             : }
     108             : 
     109             : #include <vppinfra/memcpy.h>
     110             : 
     111             : /* c-11 string manipulation variants */
     112             : 
     113             : #ifndef EOK
     114             : #define EOK 0
     115             : #endif
     116             : #ifndef EINVAL
     117             : #define EINVAL 22
     118             : #endif
     119             : #ifndef ESRCH
     120             : #define ESRCH 3
     121             : #endif
     122             : #ifndef EOVERFLOW
     123             : #define EOVERFLOW 75
     124             : #endif
     125             : 
     126             : /*
     127             :  * In order to provide smooth mapping from unsafe string API to the clib string
     128             :  * macro, we often have to improvise s1max and s2max due to the additional
     129             :  * arguments are required for implementing the safe API. This macro is used
     130             :  * to provide the s1max/s2max. It is not perfect because the actual
     131             :  * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
     132             :  * the macro would cause a regression. However, it is not terribly likely.
     133             :  * So I bet against the odds.
     134             :  */
     135             : #define CLIB_STRING_MACRO_MAX 4096
     136             : 
     137             : typedef int errno_t;
     138             : typedef uword rsize_t;
     139             : 
     140             : void clib_c11_violation (const char *s);
     141             : errno_t memcpy_s (void *__restrict__ dest, rsize_t dmax,
     142             :                   const void *__restrict__ src, rsize_t n);
     143             : 
     144             : always_inline errno_t
     145    77530838 : memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
     146             :                  const void *__restrict__ src, rsize_t n)
     147             : {
     148             :   uword low, hi;
     149             :   u8 bad;
     150             : 
     151             :   /*
     152             :    * Optimize constant-number-of-bytes calls without asking
     153             :    * "too many questions for someone from New Jersey"
     154             :    */
     155             :   if (COMPILE_TIME_CONST (n))
     156             :     {
     157             :       clib_memcpy_fast (dest, src, n);
     158             :       return EOK;
     159             :     }
     160             : 
     161             :   /*
     162             :    * call bogus if: src or dst NULL, trying to copy
     163             :    * more data than we have space in dst, or src == dst.
     164             :    * n == 0 isn't really "bad", so check first in the
     165             :    * "wall-of-shame" department...
     166             :    */
     167    77530838 :   bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
     168    77530838 :   if (PREDICT_FALSE (bad != 0))
     169             :     {
     170             :       /* Not actually trying to copy anything is OK */
     171      171160 :       if (n == 0)
     172      171160 :         return EOK;
     173           0 :       if (dest == NULL)
     174           0 :         clib_c11_violation ("dest NULL");
     175           0 :       if (src == NULL)
     176           0 :         clib_c11_violation ("src NULL");
     177           0 :       if (n > dmax)
     178           0 :         clib_c11_violation ("n > dmax");
     179           0 :       if (dest == src)
     180           0 :         clib_c11_violation ("dest == src");
     181           0 :       return EINVAL;
     182             :     }
     183             : 
     184             :   /* Check for src/dst overlap, which is not allowed */
     185    77359678 :   low = (uword) (src < dest ? src : dest);
     186    77359678 :   hi = (uword) (src < dest ? dest : src);
     187             : 
     188    77359678 :   if (PREDICT_FALSE (low + (n - 1) >= hi))
     189             :     {
     190           2 :       clib_c11_violation ("src/dest overlap");
     191           2 :       return EINVAL;
     192             :     }
     193             : 
     194    77359676 :   clib_memcpy_fast (dest, src, n);
     195    77359677 :   return EOK;
     196             : }
     197             : 
     198             : /*
     199             :  * Note: $$$ This macro is a crutch. Folks need to manually
     200             :  * inspect every extant clib_memcpy(...) call and
     201             :  * attempt to provide a real destination buffer size
     202             :  * argument...
     203             :  */
     204             : #define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
     205             : 
     206             : errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
     207             : 
     208             : always_inline errno_t
     209   279202265 : memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
     210             : {
     211             :   u8 bad;
     212             : 
     213   279202265 :   bad = (s == 0) + (n > smax);
     214             : 
     215   279202265 :   if (PREDICT_FALSE (bad != 0))
     216             :     {
     217           1 :       if (s == 0)
     218           0 :         clib_c11_violation ("s NULL");
     219           1 :       if (n > smax)
     220           1 :         clib_c11_violation ("n > smax");
     221           1 :       return (EINVAL);
     222             :     }
     223   279202264 :   memset (s, c, n);
     224   279202264 :   return (EOK);
     225             : }
     226             : 
     227             : /*
     228             :  * This macro is not [so much of] a crutch.
     229             :  * It's super-typical to write:
     230             :  *
     231             :  *   ep = pool_get (<pool>);
     232             :  *   clib_memset(ep, 0, sizeof (*ep));
     233             :  *
     234             :  * The compiler should delete the not-so useful
     235             :  * (n > smax) test. TBH the NULL pointer check isn't
     236             :  * so useful in this case, but so be it.
     237             :  */
     238             : #define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
     239             : 
     240             : static_always_inline void
     241      485127 : clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
     242             : {
     243             : #if defined (CLIB_HAVE_VEC256)
     244             :   u8x32 s0, s1, d0, d1;
     245      485127 :   u8x32 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
     246             :     18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
     247             :   };
     248      485127 :   u8x32 lv = u8x32_splat (len);
     249      485127 :   u8x32 add = u8x32_splat (32);
     250             : 
     251      485127 :   s0 = u8x32_load_unaligned (src);
     252      485127 :   s1 = u8x32_load_unaligned (src + 32);
     253      485127 :   d0 = u8x32_load_unaligned (dst);
     254      485127 :   d1 = u8x32_load_unaligned (dst + 32);
     255             : 
     256      485127 :   d0 = u8x32_blend (d0, s0, lv > mask);
     257      485127 :   u8x32_store_unaligned (d0, dst);
     258             : 
     259      485127 :   if (max_len <= 32)
     260      157998 :     return;
     261             : 
     262      327129 :   mask += add;
     263      327129 :   d1 = u8x32_blend (d1, s1, lv > mask);
     264      327129 :   u8x32_store_unaligned (d1, dst + 32);
     265             : 
     266             : #elif defined (CLIB_HAVE_VEC128)
     267             :   u8x16 s0, s1, s2, s3, d0, d1, d2, d3;
     268           0 :   u8x16 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
     269           0 :   u8x16 lv = u8x16_splat (len);
     270           0 :   u8x16 add = u8x16_splat (16);
     271             : 
     272           0 :   s0 = u8x16_load_unaligned (src);
     273           0 :   s1 = u8x16_load_unaligned (src + 16);
     274           0 :   s2 = u8x16_load_unaligned (src + 32);
     275           0 :   s3 = u8x16_load_unaligned (src + 48);
     276           0 :   d0 = u8x16_load_unaligned (dst);
     277           0 :   d1 = u8x16_load_unaligned (dst + 16);
     278           0 :   d2 = u8x16_load_unaligned (dst + 32);
     279           0 :   d3 = u8x16_load_unaligned (dst + 48);
     280             : 
     281           0 :   d0 = u8x16_blend (d0, s0, lv > mask);
     282           0 :   u8x16_store_unaligned (d0, dst);
     283             : 
     284           0 :   if (max_len <= 16)
     285           0 :     return;
     286             : 
     287           0 :   mask += add;
     288           0 :   d1 = u8x16_blend (d1, s1, lv > mask);
     289           0 :   u8x16_store_unaligned (d1, dst + 16);
     290             : 
     291           0 :   if (max_len <= 32)
     292           0 :     return;
     293             : 
     294           0 :   mask += add;
     295           0 :   d2 = u8x16_blend (d2, s2, lv > mask);
     296           0 :   u8x16_store_unaligned (d2, dst + 32);
     297             : 
     298           0 :   mask += add;
     299           0 :   d3 = u8x16_blend (d3, s3, lv > mask);
     300           0 :   u8x16_store_unaligned (d3, dst + 48);
     301             : #else
     302             :   memmove (dst, src, len);
     303             : #endif
     304             : }
     305             : 
     306             : static_always_inline void
     307      327129 : clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
     308             : {
     309      327129 :   clib_memcpy_le (dst, src, len, 64);
     310      327129 : }
     311             : 
     312             : static_always_inline void
     313      157998 : clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
     314             : {
     315      157998 :   clib_memcpy_le (dst, src, len, 32);
     316      157998 : }
     317             : 
     318             : static_always_inline void
     319             : clib_memset_u64 (void *p, u64 val, uword count)
     320             : {
     321             :   u64 *ptr = p;
     322             : #if defined(CLIB_HAVE_VEC512)
     323             :   u64x8 v512 = u64x8_splat (val);
     324             :   while (count >= 8)
     325             :     {
     326             :       u64x8_store_unaligned (v512, ptr);
     327             :       ptr += 8;
     328             :       count -= 8;
     329             :     }
     330             :   if (count == 0)
     331             :     return;
     332             : #endif
     333             : #if defined(CLIB_HAVE_VEC256)
     334             :   u64x4 v256 = u64x4_splat (val);
     335             :   while (count >= 4)
     336             :     {
     337             :       u64x4_store_unaligned (v256, ptr);
     338             :       ptr += 4;
     339             :       count -= 4;
     340             :     }
     341             :   if (count == 0)
     342             :     return;
     343             : #else
     344             : #if defined(CLIB_HAVE_VEC128)
     345             :   u64x2 v = u64x2_splat (val);
     346             : #endif
     347             :   while (count >= 4)
     348             :     {
     349             : #if defined(CLIB_HAVE_VEC128)
     350             :       u64x2_store_unaligned (v, ptr);
     351             :       u64x2_store_unaligned (v, ptr + 2);
     352             : #else
     353             :       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
     354             : #endif
     355             :       ptr += 4;
     356             :       count -= 4;
     357             :     }
     358             : #endif
     359             :   while (count--)
     360             :     ptr++[0] = val;
     361             : }
     362             : 
     363             : static_always_inline void
     364     1492932 : clib_memset_u32 (void *p, u32 val, uword count)
     365             : {
     366     1492932 :   u32 *ptr = p;
     367             : #if defined(CLIB_HAVE_VEC512)
     368           0 :   u32x16 v512 = u32x16_splat (val);
     369           0 :   while (count >= 16)
     370             :     {
     371           0 :       u32x16_store_unaligned (v512, ptr);
     372           0 :       ptr += 16;
     373           0 :       count -= 16;
     374             :     }
     375           0 :   if (count == 0)
     376           0 :     return;
     377             : #endif
     378             : #if defined(CLIB_HAVE_VEC256)
     379     1481540 :   u32x8 v256 = u32x8_splat (val);
     380     6695390 :   while (count >= 8)
     381             :     {
     382     5213850 :       u32x8_store_unaligned (v256, ptr);
     383     5213850 :       ptr += 8;
     384     5213850 :       count -= 8;
     385             :     }
     386     1481540 :   if (count == 0)
     387      247018 :     return;
     388             : #endif
     389             : #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
     390     1245912 :   u32x4 v128 = u32x4_splat (val);
     391    16621377 :   while (count >= 4)
     392             :     {
     393    15375409 :       u32x4_store_unaligned (v128, ptr);
     394    15375409 :       ptr += 4;
     395    15375409 :       count -= 4;
     396             :     }
     397             : #else
     398             :   while (count >= 4)
     399             :     {
     400             :       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
     401             :       ptr += 4;
     402             :       count -= 4;
     403             :     }
     404             : #endif
     405     3393462 :   while (count--)
     406     2147550 :     ptr++[0] = val;
     407       11392 : }
     408             : 
     409             : static_always_inline void
     410     1815402 : clib_memset_u16 (void *p, u16 val, uword count)
     411             : {
     412     1815402 :   u16 *ptr = p;
     413             : #if defined(CLIB_HAVE_VEC512)
     414           0 :   u16x32 v512 = u16x32_splat (val);
     415           0 :   while (count >= 32)
     416             :     {
     417           0 :       u16x32_store_unaligned (v512, ptr);
     418           0 :       ptr += 32;
     419           0 :       count -= 32;
     420             :     }
     421           0 :   if (count == 0)
     422           0 :     return;
     423             : #endif
     424             : #if defined(CLIB_HAVE_VEC256)
     425     1815325 :   u16x16 v256 = u16x16_splat (val);
     426     5565823 :   while (count >= 16)
     427             :     {
     428     3750499 :       u16x16_store_unaligned (v256, ptr);
     429     3750499 :       ptr += 16;
     430     3750499 :       count -= 16;
     431             :     }
     432     1815325 :   if (count == 0)
     433      141439 :     return;
     434             : #endif
     435             : #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
     436     1673967 :   u16x8 v128 = u16x8_splat (val);
     437     2206076 :   while (count >= 8)
     438             :     {
     439      532105 :       u16x8_store_unaligned (v128, ptr);
     440      532105 :       ptr += 8;
     441      532105 :       count -= 8;
     442             :     }
     443             : #else
     444             :   while (count >= 4)
     445             :     {
     446             :       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
     447             :       ptr += 4;
     448             :       count -= 4;
     449             :     }
     450             : #endif
     451     7053287 :   while (count--)
     452     5379324 :     ptr++[0] = val;
     453          77 : }
     454             : 
     455             : static_always_inline void
     456   197952302 : clib_memset_u8 (void *p, u8 val, uword count)
     457             : {
     458   197952302 :   u8 *ptr = p;
     459             : #if defined(CLIB_HAVE_VEC512)
     460           0 :   u8x64 v512 = u8x64_splat (val);
     461           0 :   while (count >= 64)
     462             :     {
     463           0 :       u8x64_store_unaligned (v512, ptr);
     464           0 :       ptr += 64;
     465           0 :       count -= 64;
     466             :     }
     467           0 :   if (count == 0)
     468           0 :     return;
     469             : #endif
     470             : #if defined(CLIB_HAVE_VEC256)
     471    51964712 :   u8x32 v256 = u8x32_splat (val);
     472    57487510 :   while (count >= 32)
     473             :     {
     474     5522798 :       u8x32_store_unaligned (v256, ptr);
     475     5522798 :       ptr += 32;
     476     5522798 :       count -= 32;
     477             :     }
     478    51964712 :   if (count == 0)
     479      169417 :     return;
     480             : #endif
     481             : #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
     482   197782885 :   u8x16 v128 = u8x16_splat (val);
     483 18087515991 :   while (count >= 16)
     484             :     {
     485 17889757328 :       u8x16_store_unaligned (v128, ptr);
     486 17889757431 :       ptr += 16;
     487 17889757431 :       count -= 16;
     488             :     }
     489             : #else
     490             :   while (count >= 4)
     491             :     {
     492             :       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
     493             :       ptr += 4;
     494             :       count -= 4;
     495             :     }
     496             : #endif
     497   981832500 :   while (count--)
     498   784050297 :     ptr++[0] = val;
     499   145987433 : }
     500             : 
     501             : 
     502             : /*
     503             :  * This macro is to provide smooth mapping from memcmp to memcmp_s.
     504             :  * memcmp has fewer parameters and fewer returns than memcmp_s.
     505             :  * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
     506             :  * we return 0 and spit out a message in the console because there is
     507             :  * no way to return the error code to the memcmp callers.
     508             :  * This condition happens when s1 or s2 is null. Please note
     509             :  * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
     510             :  * anyway. So we are consistent in this case for the comparison return
     511             :  * although we also spit out a C11 violation message in the console to
     512             :  * warn that they pass null pointers for both s1 and s2.
     513             :  * Applications are encouraged to use the cool C11 memcmp_s API to get the
     514             :  * maximum benefit out of it.
     515             :  */
     516             : #define clib_memcmp(s1,s2,m1) \
     517             :   ({ int __diff = 0;                                   \
     518             :     memcmp_s_inline (s1, m1, s2, m1, &__diff);      \
     519             :     __diff; \
     520             :   })
     521             : 
     522             : errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
     523             :                   rsize_t s2max, int *diff);
     524             : 
     525             : always_inline errno_t
     526        4236 : memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
     527             :                  int *diff)
     528             : {
     529             :   u8 bad;
     530             : 
     531        4236 :   bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
     532        4236 :     (s1max == 0);
     533             : 
     534        4236 :   if (PREDICT_FALSE (bad != 0))
     535             :     {
     536           3 :       if (s1 == NULL)
     537           2 :         clib_c11_violation ("s1 NULL");
     538           3 :       if (s2 == NULL)
     539           2 :         clib_c11_violation ("s2 NULL");
     540           3 :       if (diff == NULL)
     541           1 :         clib_c11_violation ("diff NULL");
     542           3 :       if (s2max > s1max)
     543           1 :         clib_c11_violation ("s2max > s1max");
     544           3 :       if (s2max == 0)
     545           2 :         clib_c11_violation ("s2max 0");
     546           3 :       if (s1max == 0)
     547           2 :         clib_c11_violation ("s1max 0");
     548           3 :       return EINVAL;
     549             :     }
     550             : 
     551        4233 :   if (PREDICT_FALSE (s1 == s2))
     552             :     {
     553          38 :       *diff = 0;
     554          38 :       return EOK;
     555             :     }
     556             : 
     557        4195 :   *diff = memcmp (s1, s2, s2max);
     558        4195 :   return EOK;
     559             : }
     560             : 
     561             : /*
     562             :  * This macro is to provide smooth mapping from strnlen to strnlen_s
     563             :  */
     564             : #define clib_strnlen(s,m) strnlen_s_inline(s,m)
     565             : 
     566             : size_t strnlen_s (const char *s, size_t maxsize);
     567             : 
     568             : always_inline size_t
     569     1213789 : strnlen_s_inline (const char *s, size_t maxsize)
     570             : {
     571             :   u8 bad;
     572             : 
     573     1213789 :   bad = (s == 0) + (maxsize == 0);
     574     1213789 :   if (PREDICT_FALSE (bad != 0))
     575             :     {
     576           3 :       if (s == 0)
     577           2 :         clib_c11_violation ("s NULL");
     578           3 :       if (maxsize == 0)
     579           3 :         clib_c11_violation ("maxsize 0");
     580           3 :       return 0;
     581             :     }
     582     1213788 :   return strnlen (s, maxsize);
     583             : }
     584             : 
     585             : /*
     586             :  * This macro is to provide smooth mapping from strcmp to strcmp_s.
     587             :  * strcmp has fewer parameters and fewer returns than strcmp_s.
     588             :  * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
     589             :  * we return 0 and spit out a message in the console because
     590             :  * there is no way to return the error to the strcmp callers.
     591             :  * This condition happens when s1 or s2 is null. Please note in the extant
     592             :  * strcmp call, they would end up crashing if one of them is null.
     593             :  * So the new behavior is no crash, but an error is displayed in the
     594             :  * console which I think is more user friendly. If both s1 and s2 are null,
     595             :  * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
     596             :  * to actually accessing the pointer contents. We are still consistent
     597             :  * in this case for the comparison return although we also spit out a
     598             :  * C11 violation message in the console to warn that they pass null pointers
     599             :  * for both s1 and s2. The other problem is strcmp does not provide s1max,
     600             :  * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
     601             :  * If not, we may be accessing memory beyonf what is intended.
     602             :  * Applications are encouraged to use the cool C11 strcmp_s API to get the
     603             :  * maximum benefit out of it.
     604             :  */
     605             : #define clib_strcmp(s1,s2) \
     606             :   ({ int __indicator = 0; \
     607             :     strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator);  \
     608             :     __indicator;                        \
     609             :   })
     610             : 
     611             : errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
     612             :                   int *indicator);
     613             : 
     614             : always_inline errno_t
     615      121972 : strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
     616             :                  int *indicator)
     617             : {
     618             :   u8 bad;
     619             : 
     620      243944 :   bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
     621      121972 :     (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
     622             : 
     623      121972 :   if (PREDICT_FALSE (bad != 0))
     624             :     {
     625           3 :       if (indicator == NULL)
     626           1 :         clib_c11_violation ("indicator NULL");
     627           3 :       if (s1 == NULL)
     628           2 :         clib_c11_violation ("s1 NULL");
     629           3 :       if (s2 == NULL)
     630           2 :         clib_c11_violation ("s2 NULL");
     631           3 :       if (s1max == 0)
     632           1 :         clib_c11_violation ("s1max 0");
     633           3 :       if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
     634           1 :         clib_c11_violation ("s1 unterminated");
     635           3 :       return EINVAL;
     636             :     }
     637             : 
     638      121969 :   *indicator = strcmp (s1, s2);
     639      121969 :   return EOK;
     640             : }
     641             : 
     642             : /*
     643             :  * This macro is to provide smooth mapping from strncmp to strncmp_s.
     644             :  * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
     645             :  * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
     646             :  * we return 0 and spit out a message in the console because there is no
     647             :  * means to return the error to the strncmp caller.
     648             :  * This condition happens when s1 or s2 is null. In the extant strncmp call,
     649             :  * they would end up crashing if one of them is null. So the new behavior is
     650             :  * no crash, but error is displayed in the console which is more
     651             :  * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
     652             :  * strncmp did the pointers comparison prior to actually accessing the
     653             :  * pointer contents. We are still consistent in this case for the comparison
     654             :  * return although we also spit out a C11 violation message in the console to
     655             :  * warn that they pass null pointers for both s1 and s2.
     656             :  * Applications are encouraged to use the cool C11 strncmp_s API to get the
     657             :  * maximum benefit out of it.
     658             :  */
     659             : #define clib_strncmp(s1,s2,n) \
     660             :   ({ int __indicator = 0; \
     661             :     strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator);      \
     662             :     __indicator;                        \
     663             :   })
     664             : 
     665             : errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
     666             :                    int *indicator);
     667             : 
     668             : always_inline errno_t
     669          15 : strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
     670             :                   int *indicator)
     671             : {
     672             :   u8 bad;
     673          15 :   u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
     674             : 
     675          15 :   if (PREDICT_FALSE (s1_greater_s1max && indicator))
     676             :     {
     677             :       /*
     678             :        * strcmp allows n > s1max. If indicator is non null, we can still
     679             :        * do the compare without any harm and return EINVAL as well as the
     680             :        * result in indicator.
     681             :        */
     682           2 :       clib_c11_violation ("n exceeds s1 length");
     683           2 :       *indicator = strncmp (s1, s2, n);
     684           2 :       return EINVAL;
     685             :     }
     686             : 
     687          26 :   bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
     688          13 :     (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
     689             : 
     690          13 :   if (PREDICT_FALSE (bad != 0))
     691             :     {
     692           3 :       if (indicator == NULL)
     693           1 :         clib_c11_violation ("indicator NULL");
     694           3 :       if (s1 == NULL)
     695           2 :         clib_c11_violation ("s1 NULL");
     696           3 :       if (s2 == NULL)
     697           2 :         clib_c11_violation ("s2 NULL");
     698           3 :       if (s1max == 0)
     699           1 :         clib_c11_violation ("s1max 0");
     700           3 :       if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
     701           1 :         clib_c11_violation ("s1 unterminated");
     702           3 :       if (s1_greater_s1max)
     703           0 :         clib_c11_violation ("n exceeds s1 length");
     704           3 :       return EINVAL;
     705             :     }
     706             : 
     707          10 :   *indicator = strncmp (s1, s2, n);
     708          10 :   return EOK;
     709             : }
     710             : 
     711             : errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
     712             :                   const char *__restrict__ src);
     713             : 
     714             : always_inline errno_t
     715          17 : strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
     716             :                  const char *__restrict__ src)
     717             : {
     718             :   u8 bad;
     719             :   uword low, hi;
     720             :   size_t n;
     721             : 
     722          17 :   bad = (dest == 0) + (dmax == 0) + (src == 0);
     723          17 :   if (PREDICT_FALSE (bad != 0))
     724             :     {
     725           1 :       if (dest == 0)
     726           1 :         clib_c11_violation ("dest NULL");
     727           1 :       if (src == 0)
     728           1 :         clib_c11_violation ("src NULL");
     729           1 :       if (dmax == 0)
     730           1 :         clib_c11_violation ("dmax 0");
     731           1 :       return EINVAL;
     732             :     }
     733             : 
     734          16 :   n = clib_strnlen (src, dmax);
     735          16 :   if (PREDICT_FALSE (n >= dmax))
     736             :     {
     737           1 :       clib_c11_violation ("not enough space for dest");
     738           1 :       return (EINVAL);
     739             :     }
     740             :   /* Not actually trying to copy anything is OK */
     741          15 :   if (PREDICT_FALSE (n == 0))
     742           0 :     return EOK;
     743             : 
     744             :   /* Check for src/dst overlap, which is not allowed */
     745          15 :   low = (uword) (src < dest ? src : dest);
     746          15 :   hi = (uword) (src < dest ? dest : src);
     747             : 
     748          15 :   if (PREDICT_FALSE (low + (n - 1) >= hi))
     749             :     {
     750           1 :       clib_c11_violation ("src/dest overlap");
     751           1 :       return EINVAL;
     752             :     }
     753             : 
     754          14 :   clib_memcpy_fast (dest, src, n);
     755          14 :   dest[n] = '\0';
     756          14 :   return EOK;
     757             : }
     758             : 
     759             : /*
     760             :  * This macro is provided for smooth migration from strncpy. It is not perfect
     761             :  * because we don't know the size of the destination buffer to pass to
     762             :  * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
     763             :  * Applications are encouraged to move to the C11 strncpy_s API and provide
     764             :  * the correct dmax for better error checking.
     765             :  */
     766             : #define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
     767             : 
     768             : errno_t
     769             : strncpy_s (char *__restrict__ dest, rsize_t dmax,
     770             :            const char *__restrict__ src, rsize_t n);
     771             : 
     772             : always_inline errno_t
     773     1088970 : strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
     774             :                   const char *__restrict__ src, rsize_t n)
     775             : {
     776             :   u8 bad;
     777             :   uword low, hi;
     778             :   rsize_t m;
     779     1088970 :   errno_t status = EOK;
     780             : 
     781     1088970 :   bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
     782     1088970 :   if (PREDICT_FALSE (bad != 0))
     783             :     {
     784             :       /* Not actually trying to copy anything is OK */
     785           4 :       if (n == 0)
     786           2 :         return EOK;
     787           2 :       if (dest == 0)
     788           2 :         clib_c11_violation ("dest NULL");
     789           2 :       if (src == 0)
     790           2 :         clib_c11_violation ("src NULL");
     791           2 :       if (dmax == 0)
     792           1 :         clib_c11_violation ("dmax 0");
     793           2 :       return EINVAL;
     794             :     }
     795             : 
     796     1088958 :   if (PREDICT_FALSE (n >= dmax))
     797             :     {
     798             :       /* Relax and use strnlen of src */
     799           1 :       clib_c11_violation ("n >= dmax");
     800           1 :       m = clib_strnlen (src, dmax);
     801           1 :       if (m >= dmax)
     802             :         {
     803             :           /* Truncate, adjust copy length to fit dest */
     804           1 :           m = dmax - 1;
     805           1 :           status = EOVERFLOW;
     806             :         }
     807             :     }
     808             :   else
     809             :     /* cap the copy to strlen(src) in case n > strlen(src) */
     810     1088958 :     m = clib_strnlen (src, n);
     811             : 
     812             :   /* Check for src/dst overlap, which is not allowed */
     813     1088958 :   low = (uword) (src < dest ? src : dest);
     814     1088958 :   hi = (uword) (src < dest ? dest : src);
     815             : 
     816             :   /*
     817             :    * This check may fail innocently if src + dmax >= dst, but
     818             :    * src + strlen(src) < dst. If it fails, check more carefully before
     819             :    * blowing the whistle.
     820             :    */
     821     1088958 :   if (PREDICT_FALSE (low + (m - 1) >= hi))
     822             :     {
     823           2 :       m = clib_strnlen (src, m);
     824             : 
     825           2 :       if (low + (m - 1) >= hi)
     826             :         {
     827           2 :           clib_c11_violation ("src/dest overlap");
     828           2 :           return EINVAL;
     829             :         }
     830             :     }
     831             : 
     832     1088957 :   clib_memcpy_fast (dest, src, m);
     833     1088957 :   dest[m] = '\0';
     834     1088957 :   return status;
     835             : }
     836             : 
     837             : errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
     838             :                   const char *__restrict__ src);
     839             : 
     840             : always_inline errno_t
     841           5 : strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
     842             :                  const char *__restrict__ src)
     843             : {
     844             :   u8 bad;
     845             :   uword low, hi;
     846             :   size_t m, n, dest_size;
     847             : 
     848           5 :   bad = (dest == 0) + (dmax == 0) + (src == 0);
     849           5 :   if (PREDICT_FALSE (bad != 0))
     850             :     {
     851           1 :       if (dest == 0)
     852           1 :         clib_c11_violation ("dest NULL");
     853           1 :       if (src == 0)
     854           1 :         clib_c11_violation ("src NULL");
     855           1 :       if (dmax == 0)
     856           1 :         clib_c11_violation ("dmax 0");
     857           1 :       return EINVAL;
     858             :     }
     859             : 
     860           4 :   dest_size = clib_strnlen (dest, dmax);
     861           4 :   m = dmax - dest_size;
     862           4 :   n = clib_strnlen (src, m);
     863           4 :   if (PREDICT_FALSE (n >= m))
     864             :     {
     865           1 :       clib_c11_violation ("not enough space for dest");
     866           1 :       return EINVAL;
     867             :     }
     868             : 
     869             :   /* Not actually trying to concatenate anything is OK */
     870           3 :   if (PREDICT_FALSE (n == 0))
     871           1 :     return EOK;
     872             : 
     873             :   /* Check for src/dst overlap, which is not allowed */
     874           2 :   low = (uword) (src < dest ? src : dest);
     875           2 :   hi = (uword) (src < dest ? dest : src);
     876             : 
     877           2 :   if (PREDICT_FALSE (low + (n - 1) >= hi))
     878             :     {
     879           1 :       clib_c11_violation ("src/dest overlap");
     880           1 :       return EINVAL;
     881             :     }
     882             : 
     883           1 :   clib_memcpy_fast (dest + dest_size, src, n);
     884           1 :   dest[dest_size + n] = '\0';
     885           1 :   return EOK;
     886             : }
     887             : 
     888             : errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
     889             :                    const char *__restrict__ src, rsize_t n);
     890             : 
     891             : always_inline errno_t
     892           9 : strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
     893             :                   const char *__restrict__ src, rsize_t n)
     894             : {
     895             :   u8 bad;
     896             :   uword low, hi;
     897             :   size_t m, dest_size, allowed_size;
     898           9 :   errno_t status = EOK;
     899             : 
     900           9 :   bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
     901           9 :   if (PREDICT_FALSE (bad != 0))
     902             :     {
     903             :       /* Not actually trying to concatenate anything is OK */
     904           2 :       if (n == 0)
     905           1 :         return EOK;
     906           1 :       if (dest == 0)
     907           1 :         clib_c11_violation ("dest NULL");
     908           1 :       if (src == 0)
     909           1 :         clib_c11_violation ("src NULL");
     910           1 :       if (dmax == 0)
     911           1 :         clib_c11_violation ("dmax 0");
     912           1 :       return EINVAL;
     913             :     }
     914             : 
     915             :   /* Check for src/dst overlap, which is not allowed */
     916           7 :   low = (uword) (src < dest ? src : dest);
     917           7 :   hi = (uword) (src < dest ? dest : src);
     918             : 
     919           7 :   if (PREDICT_FALSE (low + (n - 1) >= hi))
     920             :     {
     921           1 :       clib_c11_violation ("src/dest overlap");
     922           1 :       return EINVAL;
     923             :     }
     924             : 
     925           6 :   dest_size = clib_strnlen (dest, dmax);
     926           6 :   allowed_size = dmax - dest_size;
     927             : 
     928           6 :   if (PREDICT_FALSE (allowed_size == 0))
     929             :     {
     930           1 :       clib_c11_violation ("no space left in dest");
     931           1 :       return (EINVAL);
     932             :     }
     933             : 
     934           5 :   if (PREDICT_FALSE (n >= allowed_size))
     935             :     {
     936             :       /*
     937             :        * unlike strcat_s, strncat_s will do the concatenation anyway when
     938             :        * there is not enough space in dest. But it will do the truncation and
     939             :        * null terminate dest
     940             :        */
     941           1 :       m = clib_strnlen (src, allowed_size);
     942           1 :       if (m >= allowed_size)
     943             :         {
     944           1 :           m = allowed_size - 1;
     945           1 :           status = EOVERFLOW;
     946             :         }
     947             :     }
     948             :   else
     949           4 :     m = clib_strnlen (src, n);
     950             : 
     951           5 :   clib_memcpy_fast (dest + dest_size, src, m);
     952           5 :   dest[dest_size + m] = '\0';
     953           5 :   return status;
     954             : }
     955             : 
     956             : /*
     957             :  * This macro is to provide smooth mapping from strtok_r to strtok_s.
     958             :  * To map strtok to this macro, the caller would have to supply an additional
     959             :  * argument. strtokr_s requires s1max which the unsafe API does not have. So
     960             :  * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
     961             :  * this macro cannot catch unterminated s1 and s2.
     962             :  * Applications are encouraged to use the cool C11 strtok_s API to avoid
     963             :  * these problems.
     964             :  */
     965             : #define clib_strtok(s1,s2,p)               \
     966             :   ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX;   \
     967             :     strtok_s_inline (s1, &__s1max, s2, p);          \
     968             :   })
     969             : 
     970             : char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
     971             :                 const char *__restrict__ s2, char **__restrict__ ptr);
     972             : 
     973             : always_inline char *
     974          20 : strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
     975             :                  const char *__restrict__ s2, char **__restrict__ ptr)
     976             : {
     977             : #define STRTOK_DELIM_MAX_LEN 16
     978             :   u8 bad;
     979             :   const char *pt;
     980             :   char *ptoken;
     981             :   uword dlen, slen;
     982             : 
     983          40 :   bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
     984          20 :     ((s1 == 0) && ptr && (*ptr == 0));
     985          20 :   if (PREDICT_FALSE (bad != 0))
     986             :     {
     987           2 :       if (s2 == NULL)
     988           2 :         clib_c11_violation ("s2 NULL");
     989           2 :       if (s1max == NULL)
     990           2 :         clib_c11_violation ("s1max is NULL");
     991           2 :       if (ptr == NULL)
     992           1 :         clib_c11_violation ("ptr is NULL");
     993             :       /* s1 == 0 and *ptr == null is no good */
     994           2 :       if ((s1 == 0) && ptr && (*ptr == 0))
     995           1 :         clib_c11_violation ("s1 and ptr contents are NULL");
     996           2 :       return 0;
     997             :     }
     998             : 
     999          18 :   if (s1 == 0)
    1000          12 :     s1 = *ptr;
    1001             : 
    1002             :   /*
    1003             :    * scan s1 for a delimiter
    1004             :    */
    1005          18 :   dlen = *s1max;
    1006          18 :   ptoken = 0;
    1007          33 :   while (*s1 != '\0' && !ptoken)
    1008             :     {
    1009          16 :       if (PREDICT_FALSE (dlen == 0))
    1010             :         {
    1011           0 :           *ptr = 0;
    1012           0 :           clib_c11_violation ("s1 unterminated");
    1013           0 :           return 0;
    1014             :         }
    1015             : 
    1016             :       /*
    1017             :        * must scan the entire delimiter list
    1018             :        * ISO should have included a delimiter string limit!!
    1019             :        */
    1020          16 :       slen = STRTOK_DELIM_MAX_LEN;
    1021          16 :       pt = s2;
    1022          47 :       while (*pt != '\0')
    1023             :         {
    1024          32 :           if (PREDICT_FALSE (slen == 0))
    1025             :             {
    1026           1 :               *ptr = 0;
    1027           1 :               clib_c11_violation ("s2 unterminated");
    1028           1 :               return 0;
    1029             :             }
    1030          31 :           slen--;
    1031          31 :           if (*s1 == *pt)
    1032             :             {
    1033           0 :               ptoken = 0;
    1034           0 :               break;
    1035             :             }
    1036             :           else
    1037             :             {
    1038          31 :               pt++;
    1039          31 :               ptoken = s1;
    1040             :             }
    1041             :         }
    1042          15 :       s1++;
    1043          15 :       dlen--;
    1044             :     }
    1045             : 
    1046             :   /*
    1047             :    * if the beginning of a token was not found, then no
    1048             :    * need to continue the scan.
    1049             :    */
    1050          17 :   if (ptoken == 0)
    1051             :     {
    1052           2 :       *s1max = dlen;
    1053           2 :       return (ptoken);
    1054             :     }
    1055             : 
    1056             :   /*
    1057             :    * Now we need to locate the end of the token
    1058             :    */
    1059         120 :   while (*s1 != '\0')
    1060             :     {
    1061         116 :       if (dlen == 0)
    1062             :         {
    1063           1 :           *ptr = 0;
    1064           1 :           clib_c11_violation ("s1 unterminated");
    1065           1 :           return 0;
    1066             :         }
    1067             : 
    1068         115 :       slen = STRTOK_DELIM_MAX_LEN;
    1069         115 :       pt = s2;
    1070         220 :       while (*pt != '\0')
    1071             :         {
    1072         115 :           if (slen == 0)
    1073             :             {
    1074           0 :               *ptr = 0;
    1075           0 :               clib_c11_violation ("s2 unterminated");
    1076           0 :               return 0;
    1077             :             }
    1078         115 :           slen--;
    1079         115 :           if (*s1 == *pt)
    1080             :             {
    1081             :               /*
    1082             :                * found a delimiter, set to null
    1083             :                * and return context ptr to next char
    1084             :                */
    1085          10 :               *s1 = '\0';
    1086          10 :               *ptr = (s1 + 1);  /* return pointer for next scan */
    1087          10 :               *s1max = dlen - 1;        /* account for the nulled delimiter */
    1088          10 :               return (ptoken);
    1089             :             }
    1090             :           else
    1091             :             {
    1092             :               /*
    1093             :                * simply scanning through the delimiter string
    1094             :                */
    1095         105 :               pt++;
    1096             :             }
    1097             :         }
    1098         105 :       s1++;
    1099         105 :       dlen--;
    1100             :     }
    1101             : 
    1102           4 :   *ptr = s1;
    1103           4 :   *s1max = dlen;
    1104           4 :   return (ptoken);
    1105             : }
    1106             : 
    1107             : errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
    1108             :                   char **substring);
    1109             : 
    1110             : always_inline errno_t
    1111           4 : strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
    1112             :                  char **substring)
    1113             : {
    1114             :   u8 bad;
    1115             :   size_t s1_size, s2_size;
    1116             : 
    1117           4 :   bad =
    1118           8 :     (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
    1119           4 :     (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
    1120           4 :     (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
    1121           4 :   if (PREDICT_FALSE (bad != 0))
    1122             :     {
    1123           2 :       if (s1 == 0)
    1124           1 :         clib_c11_violation ("s1 NULL");
    1125           2 :       if (s2 == 0)
    1126           1 :         clib_c11_violation ("s2 NULL");
    1127           2 :       if (s1max == 0)
    1128           1 :         clib_c11_violation ("s1max 0");
    1129           2 :       if (s2max == 0)
    1130           1 :         clib_c11_violation ("s2max 0");
    1131           2 :       if (substring == 0)
    1132           1 :         clib_c11_violation ("substring NULL");
    1133           2 :       if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
    1134           1 :         clib_c11_violation ("s1 unterminated");
    1135           2 :       if (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'))
    1136           1 :         clib_c11_violation ("s2 unterminated");
    1137           2 :       return EINVAL;
    1138             :     }
    1139             : 
    1140             :   /*
    1141             :    * s2 points to a string with zero length, or s2 equals s1, return s1
    1142             :    */
    1143           2 :   if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
    1144             :     {
    1145           0 :       *substring = s1;
    1146           0 :       return EOK;
    1147             :     }
    1148             : 
    1149             :   /*
    1150             :    * s2_size > s1_size, it won't find match.
    1151             :    */
    1152           2 :   s1_size = clib_strnlen (s1, s1max);
    1153           2 :   s2_size = clib_strnlen (s2, s2max);
    1154           2 :   if (PREDICT_FALSE (s2_size > s1_size))
    1155           0 :     return ESRCH;
    1156             : 
    1157           2 :   *substring = strstr (s1, s2);
    1158           2 :   if (*substring == 0)
    1159           1 :     return ESRCH;
    1160             : 
    1161           1 :   return EOK;
    1162             : }
    1163             : 
    1164             : #endif /* included_clib_string_h */
    1165             : 
    1166             : /*
    1167             :  * fd.io coding-style-patch-verification: ON
    1168             :  *
    1169             :  * Local Variables:
    1170             :  * eval: (c-set-style "gnu")
    1171             :  * End:
    1172             :  */

Generated by: LCOV version 1.14