xref: /freebsd/lib/libc/string/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/limits.h>
32 #include <sys/types.h>
33 #include <string.h>
34 
35 /*
36  * Portable strlen() for 32-bit and 64-bit systems.
37  *
38  * The expression:
39  *
40  *	((x - 0x01....01) & ~x & 0x80....80)
41  *
42  * would evaluate to a non-zero value iff any of the bytes in the
43  * original word is zero.
44  *
45  * The algorithm above is found on "Hacker's Delight" by
46  * Henry S. Warren, Jr.
47  *
48  * Note: this leaves performance on the table and each architecture
49  * would be best served with a tailor made routine instead.
50  */
51 
52 #if LONG_BIT == 32
53 static const unsigned long mask01 = 0x01010101;
54 static const unsigned long mask80 = 0x80808080;
55 #elif LONG_BIT == 64
56 static const unsigned long mask01 = 0x0101010101010101;
57 static const unsigned long mask80 = 0x8080808080808080;
58 #else
59 #error Unsupported word size
60 #endif
61 
62 #define	LONGPTR_MASK (sizeof(long) - 1)
63 
64 #if BYTE_ORDER == LITTLE_ENDIAN
65 #define	FINDZERO __builtin_ctzl
66 #else
67 #define	FINDZERO __builtin_clzl
68 #endif
69 
70 size_t
71 strlen(const char *str)
72 {
73 	const unsigned long *lp;
74 	unsigned long mask;
75 	long va, vb;
76 	long val;
77 
78 	lp = (unsigned long *) (uintptr_t) str;
79 	if ((uintptr_t)lp & LONGPTR_MASK) {
80 		lp = (__typeof(lp)) ((uintptr_t)lp & ~LONGPTR_MASK);
81 #if BYTE_ORDER == LITTLE_ENDIAN
82 		mask = ~(~0UL << (((uintptr_t)str & LONGPTR_MASK) << 3));
83 #else
84 		mask = ~(~0UL >> (((uintptr_t)str & LONGPTR_MASK) << 3));
85 #endif
86 		val = *lp | mask;
87 		va = (val - mask01);
88 		vb = ((~val) & mask80);
89 		if (va & vb) {
90 			return ((const char *)lp - str + (FINDZERO(va & vb) >> 3));
91 		}
92 		lp++;
93 	}
94 
95 	for (; ; lp++) {
96 		va = (*lp - mask01);
97 		vb = ((~*lp) & mask80);
98 		if (va & vb) {
99 			return ((const char *)lp - str + (FINDZERO(va & vb) >> 3));
100 		}
101 	}
102 
103 	__builtin_unreachable();
104 	return (0);
105 }
106