xref: /freebsd/sys/libkern/strlen.c (revision 69c5fa5cd1ec9b09ed88a086607a8a0993818db9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009, 2010 Xin LI <delphij@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/libkern.h>
32 #include <sys/limits.h>
33 
34 /*
35  * Portable strlen() for 32-bit and 64-bit systems.
36  *
37  * The expression:
38  *
39  *	((x - 0x01....01) & ~x & 0x80....80)
40  *
41  * would evaluate to a non-zero value iff any of the bytes in the
42  * original word is zero.
43  *
44  * The algorithm above is found on "Hacker's Delight" by
45  * Henry S. Warren, Jr.
46  */
47 
48 #if LONG_BIT == 32
49 static const unsigned long mask01 = 0x01010101;
50 static const unsigned long mask80 = 0x80808080;
51 #elif LONG_BIT == 64
52 static const unsigned long mask01 = 0x0101010101010101;
53 static const unsigned long mask80 = 0x8080808080808080;
54 #else
55 #error Unsupported word size
56 #endif
57 
58 #define	LONGPTR_MASK (sizeof(long) - 1)
59 
60 #if BYTE_ORDER == LITTLE_ENDIAN
61 #define	FINDZERO __builtin_ctzl
62 #else
63 #define	FINDZERO __builtin_clzl
64 #endif
65 
66 size_t
67 (strlen)(const char *str)
68 {
69 	const unsigned long *lp;
70 	unsigned long mask;
71 	long va, vb;
72 	long val;
73 
74 	lp = (unsigned long *) (uintptr_t) str;
75 	if ((uintptr_t)lp & LONGPTR_MASK) {
76 		lp = (__typeof(lp)) ((uintptr_t)lp & ~LONGPTR_MASK);
77 #if BYTE_ORDER == LITTLE_ENDIAN
78 		mask = ~(~0UL << (((uintptr_t)str & LONGPTR_MASK) << 3));
79 #else
80 		mask = ~(~0UL >> (((uintptr_t)str & LONGPTR_MASK) << 3));
81 #endif
82 		val = *lp | mask;
83 		va = (val - mask01);
84 		vb = ((~val) & mask80);
85 		if (va & vb) {
86 			return ((const char *)lp - str + (FINDZERO(va & vb) >> 3));
87 		}
88 		lp++;
89 	}
90 
91 	for (; ; lp++) {
92 		va = (*lp - mask01);
93 		vb = ((~*lp) & mask80);
94 		if (va & vb) {
95 			return ((const char *)lp - str + (FINDZERO(va & vb) >> 3));
96 		}
97 	}
98 
99 	__builtin_unreachable();
100 	return (0);
101 }
102