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