vppinfra: small optimization in clib_memset_u64
[vpp.git] / src / vppinfra / string.h
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 #include <vppinfra/memcpy_x86_64.h>
51
52 #ifdef CLIB_LINUX_KERNEL
53 #include <linux/string.h>
54 #endif
55
56 #ifdef CLIB_UNIX
57 #include <string.h>
58 #endif
59
60 #ifdef CLIB_STANDALONE
61 #include <vppinfra/standalone_string.h>
62 #endif
63
64 #if _x86_64_
65 #include <x86intrin.h>
66 #endif
67
68 /* Exchanges source and destination. */
69 void clib_memswap (void *_a, void *_b, uword bytes);
70
71
72 static_always_inline void *
73 clib_memcpy_fast (void *restrict dst, const void *restrict src, size_t n)
74 {
75   ASSERT (dst && src &&
76           "memcpy(src, dst, n) with src == NULL or dst == NULL is undefined "
77           "behaviour");
78 #if defined(__COVERITY__)
79   return memcpy (dst, src, n);
80 #elif defined(__x86_64__)
81   clib_memcpy_x86_64 (dst, src, n);
82   return dst;
83 #else
84   return memcpy (dst, src, n);
85 #endif
86 }
87
88 #include <vppinfra/memcpy.h>
89
90 /* c-11 string manipulation variants */
91
92 #ifndef EOK
93 #define EOK 0
94 #endif
95 #ifndef EINVAL
96 #define EINVAL 22
97 #endif
98 #ifndef ESRCH
99 #define ESRCH 3
100 #endif
101 #ifndef EOVERFLOW
102 #define EOVERFLOW 75
103 #endif
104
105 /*
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.
113  */
114 #define CLIB_STRING_MACRO_MAX 4096
115
116 typedef int errno_t;
117 typedef uword rsize_t;
118
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);
122
123 always_inline errno_t
124 memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
125                  const void *__restrict__ src, rsize_t n)
126 {
127   uword low, hi;
128   u8 bad;
129
130   /*
131    * Optimize constant-number-of-bytes calls without asking
132    * "too many questions for someone from New Jersey"
133    */
134   if (COMPILE_TIME_CONST (n))
135     {
136       clib_memcpy_fast (dest, src, n);
137       return EOK;
138     }
139
140   /*
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...
145    */
146   bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
147   if (PREDICT_FALSE (bad != 0))
148     {
149       /* Not actually trying to copy anything is OK */
150       if (n == 0)
151         return EOK;
152       if (dest == NULL)
153         clib_c11_violation ("dest NULL");
154       if (src == NULL)
155         clib_c11_violation ("src NULL");
156       if (n > dmax)
157         clib_c11_violation ("n > dmax");
158       if (dest == src)
159         clib_c11_violation ("dest == src");
160       return EINVAL;
161     }
162
163   /* Check for src/dst overlap, which is not allowed */
164   low = (uword) (src < dest ? src : dest);
165   hi = (uword) (src < dest ? dest : src);
166
167   if (PREDICT_FALSE (low + (n - 1) >= hi))
168     {
169       clib_c11_violation ("src/dest overlap");
170       return EINVAL;
171     }
172
173   clib_memcpy_fast (dest, src, n);
174   return EOK;
175 }
176
177 /*
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
181  * argument...
182  */
183 #define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
184
185 errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
186
187 always_inline errno_t
188 memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
189 {
190   u8 bad;
191
192   bad = (s == 0) + (n > smax);
193
194   if (PREDICT_FALSE (bad != 0))
195     {
196       if (s == 0)
197         clib_c11_violation ("s NULL");
198       if (n > smax)
199         clib_c11_violation ("n > smax");
200       return (EINVAL);
201     }
202   memset (s, c, n);
203   return (EOK);
204 }
205
206 /*
207  * This macro is not [so much of] a crutch.
208  * It's super-typical to write:
209  *
210  *   ep = pool_get (<pool>);
211  *   clib_memset(ep, 0, sizeof (*ep));
212  *
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.
216  */
217 #define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
218
219 static_always_inline void
220 clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
221 {
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
226   };
227   u8x32 lv = u8x32_splat (len);
228   u8x32 add = u8x32_splat (32);
229
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);
234
235   d0 = u8x32_blend (d0, s0, lv > mask);
236   u8x32_store_unaligned (d0, dst);
237
238   if (max_len <= 32)
239     return;
240
241   mask += add;
242   d1 = u8x32_blend (d1, s1, lv > mask);
243   u8x32_store_unaligned (d1, dst + 32);
244
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);
250
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);
259
260   d0 = u8x16_blend (d0, s0, lv > mask);
261   u8x16_store_unaligned (d0, dst);
262
263   if (max_len <= 16)
264     return;
265
266   mask += add;
267   d1 = u8x16_blend (d1, s1, lv > mask);
268   u8x16_store_unaligned (d1, dst + 16);
269
270   if (max_len <= 32)
271     return;
272
273   mask += add;
274   d2 = u8x16_blend (d2, s2, lv > mask);
275   u8x16_store_unaligned (d2, dst + 32);
276
277   mask += add;
278   d3 = u8x16_blend (d3, s3, lv > mask);
279   u8x16_store_unaligned (d3, dst + 48);
280 #else
281   memmove (dst, src, len);
282 #endif
283 }
284
285 static_always_inline void
286 clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
287 {
288   clib_memcpy_le (dst, src, len, 64);
289 }
290
291 static_always_inline void
292 clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
293 {
294   clib_memcpy_le (dst, src, len, 32);
295 }
296
297 static_always_inline void
298 clib_memset_u64 (void *p, u64 val, uword count)
299 {
300   u64 *ptr = p;
301 #if defined(CLIB_HAVE_VEC512)
302   u64x8 v512 = u64x8_splat (val);
303   while (count >= 8)
304     {
305       u64x8_store_unaligned (v512, ptr);
306       ptr += 8;
307       count -= 8;
308     }
309   if (count == 0)
310     return;
311 #endif
312 #if defined(CLIB_HAVE_VEC256)
313   u64x4 v256 = u64x4_splat (val);
314   while (count >= 4)
315     {
316       u64x4_store_unaligned (v256, ptr);
317       ptr += 4;
318       count -= 4;
319     }
320   if (count == 0)
321     return;
322 #else
323 #if defined(CLIB_HAVE_VEC128)
324   u64x2 v = u64x2_splat (val);
325 #endif
326   while (count >= 4)
327     {
328 #if defined(CLIB_HAVE_VEC128)
329       u64x2_store_unaligned (v, ptr);
330       u64x2_store_unaligned (v, ptr + 2);
331 #else
332       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
333 #endif
334       ptr += 4;
335       count -= 4;
336     }
337 #endif
338   while (count--)
339     ptr++[0] = val;
340 }
341
342 static_always_inline void
343 clib_memset_u32 (void *p, u32 val, uword count)
344 {
345   u32 *ptr = p;
346 #if defined(CLIB_HAVE_VEC512)
347   u32x16 v512 = u32x16_splat (val);
348   while (count >= 16)
349     {
350       u32x16_store_unaligned (v512, ptr);
351       ptr += 16;
352       count -= 16;
353     }
354   if (count == 0)
355     return;
356 #endif
357 #if defined(CLIB_HAVE_VEC256)
358   u32x8 v256 = u32x8_splat (val);
359   while (count >= 8)
360     {
361       u32x8_store_unaligned (v256, ptr);
362       ptr += 8;
363       count -= 8;
364     }
365   if (count == 0)
366     return;
367 #endif
368 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
369   u32x4 v128 = u32x4_splat (val);
370   while (count >= 4)
371     {
372       u32x4_store_unaligned (v128, ptr);
373       ptr += 4;
374       count -= 4;
375     }
376 #else
377   while (count >= 4)
378     {
379       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
380       ptr += 4;
381       count -= 4;
382     }
383 #endif
384   while (count--)
385     ptr++[0] = val;
386 }
387
388 static_always_inline void
389 clib_memset_u16 (void *p, u16 val, uword count)
390 {
391   u16 *ptr = p;
392 #if defined(CLIB_HAVE_VEC512)
393   u16x32 v512 = u16x32_splat (val);
394   while (count >= 32)
395     {
396       u16x32_store_unaligned (v512, ptr);
397       ptr += 32;
398       count -= 32;
399     }
400   if (count == 0)
401     return;
402 #endif
403 #if defined(CLIB_HAVE_VEC256)
404   u16x16 v256 = u16x16_splat (val);
405   while (count >= 16)
406     {
407       u16x16_store_unaligned (v256, ptr);
408       ptr += 16;
409       count -= 16;
410     }
411   if (count == 0)
412     return;
413 #endif
414 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
415   u16x8 v128 = u16x8_splat (val);
416   while (count >= 8)
417     {
418       u16x8_store_unaligned (v128, ptr);
419       ptr += 8;
420       count -= 8;
421     }
422 #else
423   while (count >= 4)
424     {
425       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
426       ptr += 4;
427       count -= 4;
428     }
429 #endif
430   while (count--)
431     ptr++[0] = val;
432 }
433
434 static_always_inline void
435 clib_memset_u8 (void *p, u8 val, uword count)
436 {
437   u8 *ptr = p;
438 #if defined(CLIB_HAVE_VEC512)
439   u8x64 v512 = u8x64_splat (val);
440   while (count >= 64)
441     {
442       u8x64_store_unaligned (v512, ptr);
443       ptr += 64;
444       count -= 64;
445     }
446   if (count == 0)
447     return;
448 #endif
449 #if defined(CLIB_HAVE_VEC256)
450   u8x32 v256 = u8x32_splat (val);
451   while (count >= 32)
452     {
453       u8x32_store_unaligned (v256, ptr);
454       ptr += 32;
455       count -= 32;
456     }
457   if (count == 0)
458     return;
459 #endif
460 #if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
461   u8x16 v128 = u8x16_splat (val);
462   while (count >= 16)
463     {
464       u8x16_store_unaligned (v128, ptr);
465       ptr += 16;
466       count -= 16;
467     }
468 #else
469   while (count >= 4)
470     {
471       ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
472       ptr += 4;
473       count -= 4;
474     }
475 #endif
476   while (count--)
477     ptr++[0] = val;
478 }
479
480
481 /*
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.
494  */
495 #define clib_memcmp(s1,s2,m1) \
496   ({ int __diff = 0;                                   \
497     memcmp_s_inline (s1, m1, s2, m1, &__diff);  \
498     __diff; \
499   })
500
501 errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
502                   rsize_t s2max, int *diff);
503
504 always_inline errno_t
505 memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
506                  int *diff)
507 {
508   u8 bad;
509
510   bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
511     (s1max == 0);
512
513   if (PREDICT_FALSE (bad != 0))
514     {
515       if (s1 == NULL)
516         clib_c11_violation ("s1 NULL");
517       if (s2 == NULL)
518         clib_c11_violation ("s2 NULL");
519       if (diff == NULL)
520         clib_c11_violation ("diff NULL");
521       if (s2max > s1max)
522         clib_c11_violation ("s2max > s1max");
523       if (s2max == 0)
524         clib_c11_violation ("s2max 0");
525       if (s1max == 0)
526         clib_c11_violation ("s1max 0");
527       return EINVAL;
528     }
529
530   if (PREDICT_FALSE (s1 == s2))
531     {
532       *diff = 0;
533       return EOK;
534     }
535
536   *diff = memcmp (s1, s2, s2max);
537   return EOK;
538 }
539
540 /*
541  * This macro is to provide smooth mapping from strnlen to strnlen_s
542  */
543 #define clib_strnlen(s,m) strnlen_s_inline(s,m)
544
545 size_t strnlen_s (const char *s, size_t maxsize);
546
547 always_inline size_t
548 strnlen_s_inline (const char *s, size_t maxsize)
549 {
550   u8 bad;
551
552   bad = (s == 0) + (maxsize == 0);
553   if (PREDICT_FALSE (bad != 0))
554     {
555       if (s == 0)
556         clib_c11_violation ("s NULL");
557       if (maxsize == 0)
558         clib_c11_violation ("maxsize 0");
559       return 0;
560     }
561   return strnlen (s, maxsize);
562 }
563
564 /*
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.
583  */
584 #define clib_strcmp(s1,s2) \
585   ({ int __indicator = 0; \
586     strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator);      \
587     __indicator;                        \
588   })
589
590 errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
591                   int *indicator);
592
593 always_inline errno_t
594 strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
595                  int *indicator)
596 {
597   u8 bad;
598
599   bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
600     (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
601
602   if (PREDICT_FALSE (bad != 0))
603     {
604       if (indicator == NULL)
605         clib_c11_violation ("indicator NULL");
606       if (s1 == NULL)
607         clib_c11_violation ("s1 NULL");
608       if (s2 == NULL)
609         clib_c11_violation ("s2 NULL");
610       if (s1max == 0)
611         clib_c11_violation ("s1max 0");
612       if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
613         clib_c11_violation ("s1 unterminated");
614       return EINVAL;
615     }
616
617   *indicator = strcmp (s1, s2);
618   return EOK;
619 }
620
621 /*
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.
637  */
638 #define clib_strncmp(s1,s2,n) \
639   ({ int __indicator = 0; \
640     strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator);  \
641     __indicator;                        \
642   })
643
644 errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
645                    int *indicator);
646
647 always_inline errno_t
648 strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
649                   int *indicator)
650 {
651   u8 bad;
652   u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
653
654   if (PREDICT_FALSE (s1_greater_s1max && indicator))
655     {
656       /*
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.
660        */
661       clib_c11_violation ("n exceeds s1 length");
662       *indicator = strncmp (s1, s2, n);
663       return EINVAL;
664     }
665
666   bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
667     (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
668
669   if (PREDICT_FALSE (bad != 0))
670     {
671       if (indicator == NULL)
672         clib_c11_violation ("indicator NULL");
673       if (s1 == NULL)
674         clib_c11_violation ("s1 NULL");
675       if (s2 == NULL)
676         clib_c11_violation ("s2 NULL");
677       if (s1max == 0)
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");
683       return EINVAL;
684     }
685
686   *indicator = strncmp (s1, s2, n);
687   return EOK;
688 }
689
690 errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
691                   const char *__restrict__ src);
692
693 always_inline errno_t
694 strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
695                  const char *__restrict__ src)
696 {
697   u8 bad;
698   uword low, hi;
699   size_t n;
700
701   bad = (dest == 0) + (dmax == 0) + (src == 0);
702   if (PREDICT_FALSE (bad != 0))
703     {
704       if (dest == 0)
705         clib_c11_violation ("dest NULL");
706       if (src == 0)
707         clib_c11_violation ("src NULL");
708       if (dmax == 0)
709         clib_c11_violation ("dmax 0");
710       return EINVAL;
711     }
712
713   n = clib_strnlen (src, dmax);
714   if (PREDICT_FALSE (n >= dmax))
715     {
716       clib_c11_violation ("not enough space for dest");
717       return (EINVAL);
718     }
719   /* Not actually trying to copy anything is OK */
720   if (PREDICT_FALSE (n == 0))
721     return EOK;
722
723   /* Check for src/dst overlap, which is not allowed */
724   low = (uword) (src < dest ? src : dest);
725   hi = (uword) (src < dest ? dest : src);
726
727   if (PREDICT_FALSE (low + (n - 1) >= hi))
728     {
729       clib_c11_violation ("src/dest overlap");
730       return EINVAL;
731     }
732
733   clib_memcpy_fast (dest, src, n);
734   dest[n] = '\0';
735   return EOK;
736 }
737
738 /*
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.
744  */
745 #define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
746
747 errno_t
748 strncpy_s (char *__restrict__ dest, rsize_t dmax,
749            const char *__restrict__ src, rsize_t n);
750
751 always_inline errno_t
752 strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
753                   const char *__restrict__ src, rsize_t n)
754 {
755   u8 bad;
756   uword low, hi;
757   rsize_t m;
758   errno_t status = EOK;
759
760   bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
761   if (PREDICT_FALSE (bad != 0))
762     {
763       /* Not actually trying to copy anything is OK */
764       if (n == 0)
765         return EOK;
766       if (dest == 0)
767         clib_c11_violation ("dest NULL");
768       if (src == 0)
769         clib_c11_violation ("src NULL");
770       if (dmax == 0)
771         clib_c11_violation ("dmax 0");
772       return EINVAL;
773     }
774
775   if (PREDICT_FALSE (n >= dmax))
776     {
777       /* Relax and use strnlen of src */
778       clib_c11_violation ("n >= dmax");
779       m = clib_strnlen (src, dmax);
780       if (m >= dmax)
781         {
782           /* Truncate, adjust copy length to fit dest */
783           m = dmax - 1;
784           status = EOVERFLOW;
785         }
786     }
787   else
788     /* cap the copy to strlen(src) in case n > strlen(src) */
789     m = clib_strnlen (src, n);
790
791   /* Check for src/dst overlap, which is not allowed */
792   low = (uword) (src < dest ? src : dest);
793   hi = (uword) (src < dest ? dest : src);
794
795   /*
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.
799    */
800   if (PREDICT_FALSE (low + (m - 1) >= hi))
801     {
802       m = clib_strnlen (src, m);
803
804       if (low + (m - 1) >= hi)
805         {
806           clib_c11_violation ("src/dest overlap");
807           return EINVAL;
808         }
809     }
810
811   clib_memcpy_fast (dest, src, m);
812   dest[m] = '\0';
813   return status;
814 }
815
816 errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
817                   const char *__restrict__ src);
818
819 always_inline errno_t
820 strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
821                  const char *__restrict__ src)
822 {
823   u8 bad;
824   uword low, hi;
825   size_t m, n, dest_size;
826
827   bad = (dest == 0) + (dmax == 0) + (src == 0);
828   if (PREDICT_FALSE (bad != 0))
829     {
830       if (dest == 0)
831         clib_c11_violation ("dest NULL");
832       if (src == 0)
833         clib_c11_violation ("src NULL");
834       if (dmax == 0)
835         clib_c11_violation ("dmax 0");
836       return EINVAL;
837     }
838
839   dest_size = clib_strnlen (dest, dmax);
840   m = dmax - dest_size;
841   n = clib_strnlen (src, m);
842   if (PREDICT_FALSE (n >= m))
843     {
844       clib_c11_violation ("not enough space for dest");
845       return EINVAL;
846     }
847
848   /* Not actually trying to concatenate anything is OK */
849   if (PREDICT_FALSE (n == 0))
850     return EOK;
851
852   /* Check for src/dst overlap, which is not allowed */
853   low = (uword) (src < dest ? src : dest);
854   hi = (uword) (src < dest ? dest : src);
855
856   if (PREDICT_FALSE (low + (n - 1) >= hi))
857     {
858       clib_c11_violation ("src/dest overlap");
859       return EINVAL;
860     }
861
862   clib_memcpy_fast (dest + dest_size, src, n);
863   dest[dest_size + n] = '\0';
864   return EOK;
865 }
866
867 errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
868                    const char *__restrict__ src, rsize_t n);
869
870 always_inline errno_t
871 strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
872                   const char *__restrict__ src, rsize_t n)
873 {
874   u8 bad;
875   uword low, hi;
876   size_t m, dest_size, allowed_size;
877   errno_t status = EOK;
878
879   bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
880   if (PREDICT_FALSE (bad != 0))
881     {
882       /* Not actually trying to concatenate anything is OK */
883       if (n == 0)
884         return EOK;
885       if (dest == 0)
886         clib_c11_violation ("dest NULL");
887       if (src == 0)
888         clib_c11_violation ("src NULL");
889       if (dmax == 0)
890         clib_c11_violation ("dmax 0");
891       return EINVAL;
892     }
893
894   /* Check for src/dst overlap, which is not allowed */
895   low = (uword) (src < dest ? src : dest);
896   hi = (uword) (src < dest ? dest : src);
897
898   if (PREDICT_FALSE (low + (n - 1) >= hi))
899     {
900       clib_c11_violation ("src/dest overlap");
901       return EINVAL;
902     }
903
904   dest_size = clib_strnlen (dest, dmax);
905   allowed_size = dmax - dest_size;
906
907   if (PREDICT_FALSE (allowed_size == 0))
908     {
909       clib_c11_violation ("no space left in dest");
910       return (EINVAL);
911     }
912
913   if (PREDICT_FALSE (n >= allowed_size))
914     {
915       /*
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
919        */
920       m = clib_strnlen (src, allowed_size);
921       if (m >= allowed_size)
922         {
923           m = allowed_size - 1;
924           status = EOVERFLOW;
925         }
926     }
927   else
928     m = clib_strnlen (src, n);
929
930   clib_memcpy_fast (dest + dest_size, src, m);
931   dest[dest_size + m] = '\0';
932   return status;
933 }
934
935 /*
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
942  * these problems.
943  */
944 #define clib_strtok(s1,s2,p)               \
945   ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX;   \
946     strtok_s_inline (s1, &__s1max, s2, p);              \
947   })
948
949 char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
950                 const char *__restrict__ s2, char **__restrict__ ptr);
951
952 always_inline char *
953 strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
954                  const char *__restrict__ s2, char **__restrict__ ptr)
955 {
956 #define STRTOK_DELIM_MAX_LEN 16
957   u8 bad;
958   const char *pt;
959   char *ptoken;
960   uword dlen, slen;
961
962   bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
963     ((s1 == 0) && ptr && (*ptr == 0));
964   if (PREDICT_FALSE (bad != 0))
965     {
966       if (s2 == NULL)
967         clib_c11_violation ("s2 NULL");
968       if (s1max == NULL)
969         clib_c11_violation ("s1max is NULL");
970       if (ptr == 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");
975       return 0;
976     }
977
978   if (s1 == 0)
979     s1 = *ptr;
980
981   /*
982    * scan s1 for a delimiter
983    */
984   dlen = *s1max;
985   ptoken = 0;
986   while (*s1 != '\0' && !ptoken)
987     {
988       if (PREDICT_FALSE (dlen == 0))
989         {
990           *ptr = 0;
991           clib_c11_violation ("s1 unterminated");
992           return 0;
993         }
994
995       /*
996        * must scan the entire delimiter list
997        * ISO should have included a delimiter string limit!!
998        */
999       slen = STRTOK_DELIM_MAX_LEN;
1000       pt = s2;
1001       while (*pt != '\0')
1002         {
1003           if (PREDICT_FALSE (slen == 0))
1004             {
1005               *ptr = 0;
1006               clib_c11_violation ("s2 unterminated");
1007               return 0;
1008             }
1009           slen--;
1010           if (*s1 == *pt)
1011             {
1012               ptoken = 0;
1013               break;
1014             }
1015           else
1016             {
1017               pt++;
1018               ptoken = s1;
1019             }
1020         }
1021       s1++;
1022       dlen--;
1023     }
1024
1025   /*
1026    * if the beginning of a token was not found, then no
1027    * need to continue the scan.
1028    */
1029   if (ptoken == 0)
1030     {
1031       *s1max = dlen;
1032       return (ptoken);
1033     }
1034
1035   /*
1036    * Now we need to locate the end of the token
1037    */
1038   while (*s1 != '\0')
1039     {
1040       if (dlen == 0)
1041         {
1042           *ptr = 0;
1043           clib_c11_violation ("s1 unterminated");
1044           return 0;
1045         }
1046
1047       slen = STRTOK_DELIM_MAX_LEN;
1048       pt = s2;
1049       while (*pt != '\0')
1050         {
1051           if (slen == 0)
1052             {
1053               *ptr = 0;
1054               clib_c11_violation ("s2 unterminated");
1055               return 0;
1056             }
1057           slen--;
1058           if (*s1 == *pt)
1059             {
1060               /*
1061                * found a delimiter, set to null
1062                * and return context ptr to next char
1063                */
1064               *s1 = '\0';
1065               *ptr = (s1 + 1);  /* return pointer for next scan */
1066               *s1max = dlen - 1;        /* account for the nulled delimiter */
1067               return (ptoken);
1068             }
1069           else
1070             {
1071               /*
1072                * simply scanning through the delimiter string
1073                */
1074               pt++;
1075             }
1076         }
1077       s1++;
1078       dlen--;
1079     }
1080
1081   *ptr = s1;
1082   *s1max = dlen;
1083   return (ptoken);
1084 }
1085
1086 errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1087                   char **substring);
1088
1089 always_inline errno_t
1090 strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1091                  char **substring)
1092 {
1093   u8 bad;
1094   size_t s1_size, s2_size;
1095
1096   bad =
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))
1101     {
1102       if (s1 == 0)
1103         clib_c11_violation ("s1 NULL");
1104       if (s2 == 0)
1105         clib_c11_violation ("s2 NULL");
1106       if (s1max == 0)
1107         clib_c11_violation ("s1max 0");
1108       if (s2max == 0)
1109         clib_c11_violation ("s2max 0");
1110       if (substring == 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");
1116       return EINVAL;
1117     }
1118
1119   /*
1120    * s2 points to a string with zero length, or s2 equals s1, return s1
1121    */
1122   if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1123     {
1124       *substring = s1;
1125       return EOK;
1126     }
1127
1128   /*
1129    * s2_size > s1_size, it won't find match.
1130    */
1131   s1_size = clib_strnlen (s1, s1max);
1132   s2_size = clib_strnlen (s2, s2max);
1133   if (PREDICT_FALSE (s2_size > s1_size))
1134     return ESRCH;
1135
1136   *substring = strstr (s1, s2);
1137   if (*substring == 0)
1138     return ESRCH;
1139
1140   return EOK;
1141 }
1142
1143 #endif /* included_clib_string_h */
1144
1145 /*
1146  * fd.io coding-style-patch-verification: ON
1147  *
1148  * Local Variables:
1149  * eval: (c-set-style "gnu")
1150  * End:
1151  */