I've translated an implementation of strlen in x86 assembly to C and added alignment checking:
#include <strings.h>
#include <string.h>
#include <stdint.h>
#define NOT_HIGH_MASK 0x080808080
#define HIGH_MASK 0x7f7f7f7f
#define LOW_MASK 0x01010101
#define mul4(x) ((x) << 2)
#define div8(x) ((x) >> 3)
size_t strlen(const char *str)
{
const char *cptr = str;
uint32_t i, *s;
size_t ctr = 0;
/* Satisfy alignment requirements */
while((uintptr_t)cptr & (sizeof(uint32_t)-1))
{
if(!*cptr)
return cptr - str;
cptr++;
}
s = (uint32_t *)cptr;
do {
i = s[ctr];
/* Mask off high bit */
i &= HIGH_MASK;
/* subtract 0x01 from each byte,
giving a set high bit if it
was zero */
i -= LOW_MASK;
ctr++;
/* Test for the set high bit
and if it is found exit */
} while(!(i &= NOT_HIGH_MASK));
/* Find the first high bit set and
create the corresponding byte index */
i = div8(ffs(i));
/* Remove the counter increment from the
loop and multiply it by 4 to find the
rest of the byte index */
ctr = mul4(ctr - 1);
/* Return the combined byte index with 1
removed so it doesn't include the null
terminator itself */
return i + ctr - 1;
}
I'm looking for general critique, but I'd also like additional information in these areas:
Portability. Is this portable? Will it work on all common platforms (x86, arm, etc.) and possibly some uncommon ones?
Comments. The comments I've put here seem to already document the code well enough, but is there any additional information I could add?
Hidden Bugs. This definitely works (and has a noticeable improvement in speed), but are there any hidden bugs relating to alignment requirements or otherwise (such as extended ASCII)? For context, this is part of a standard C/POSIX library implementation.
I might note that I'm relying on my header to use the builtin version of ffs
. This may be an issue on non-GNU compilers.
If a GCC-compatible compiler (gcc, clang, icc, etc.) is detected during the build process, the flags -std=gnu99 -O2
are appended to the build flags.
cptr - str
appear in the calculations after the main loop as well? \$\endgroup\$