Blame string/strnlen.c

Packit Service 82fcde
/* Find the length of STRING, but scan at most MAXLEN characters.
Packit Service 82fcde
   Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   Contributed by Jakub Jelinek <jakub@redhat.com>.
Packit Service 82fcde
Packit Service 82fcde
   Based on strlen written by Torbjorn Granlund (tege@sics.se),
Packit Service 82fcde
   with help from Dan Sahlin (dan@sics.se);
Packit Service 82fcde
   commentary by Jim Blandy (jimb@ai.mit.edu).
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public License as
Packit Service 82fcde
   published by the Free Software Foundation; either version 2.1 of the
Packit Service 82fcde
   License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; see the file COPYING.LIB.  If
Packit Service 82fcde
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
Packit Service 82fcde
/* Find the length of S, but scan at most MAXLEN characters.  If no
Packit Service 82fcde
   '\0' terminator is found in that many characters, return MAXLEN.  */
Packit Service 82fcde
Packit Service 82fcde
#ifdef STRNLEN
Packit Service 82fcde
# define __strnlen STRNLEN
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
size_t
Packit Service 82fcde
__strnlen (const char *str, size_t maxlen)
Packit Service 82fcde
{
Packit Service 82fcde
  const char *char_ptr, *end_ptr = str + maxlen;
Packit Service 82fcde
  const unsigned long int *longword_ptr;
Packit Service 82fcde
  unsigned long int longword, himagic, lomagic;
Packit Service 82fcde
Packit Service 82fcde
  if (maxlen == 0)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
Packit Service 82fcde
  if (__glibc_unlikely (end_ptr < str))
Packit Service 82fcde
    end_ptr = (const char *) ~0UL;
Packit Service 82fcde
Packit Service 82fcde
  /* Handle the first few characters by reading one character at a time.
Packit Service 82fcde
     Do this until CHAR_PTR is aligned on a longword boundary.  */
Packit Service 82fcde
  for (char_ptr = str; ((unsigned long int) char_ptr
Packit Service 82fcde
			& (sizeof (longword) - 1)) != 0;
Packit Service 82fcde
       ++char_ptr)
Packit Service 82fcde
    if (*char_ptr == '\0')
Packit Service 82fcde
      {
Packit Service 82fcde
	if (char_ptr > end_ptr)
Packit Service 82fcde
	  char_ptr = end_ptr;
Packit Service 82fcde
	return char_ptr - str;
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  /* All these elucidatory comments refer to 4-byte longwords,
Packit Service 82fcde
     but the theory applies equally well to 8-byte longwords.  */
Packit Service 82fcde
Packit Service 82fcde
  longword_ptr = (unsigned long int *) char_ptr;
Packit Service 82fcde
Packit Service 82fcde
  /* Bits 31, 24, 16, and 8 of this number are zero.  Call these bits
Packit Service 82fcde
     the "holes."  Note that there is a hole just to the left of
Packit Service 82fcde
     each byte, with an extra at the end:
Packit Service 82fcde
Packit Service 82fcde
     bits:  01111110 11111110 11111110 11111111
Packit Service 82fcde
     bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
Packit Service 82fcde
Packit Service 82fcde
     The 1-bits make sure that carries propagate to the next 0-bit.
Packit Service 82fcde
     The 0-bits provide holes for carries to fall into.  */
Packit Service 82fcde
  himagic = 0x80808080L;
Packit Service 82fcde
  lomagic = 0x01010101L;
Packit Service 82fcde
  if (sizeof (longword) > 4)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* 64-bit version of the magic.  */
Packit Service 82fcde
      /* Do the shift in two steps to avoid a warning if long has 32 bits.  */
Packit Service 82fcde
      himagic = ((himagic << 16) << 16) | himagic;
Packit Service 82fcde
      lomagic = ((lomagic << 16) << 16) | lomagic;
Packit Service 82fcde
    }
Packit Service 82fcde
  if (sizeof (longword) > 8)
Packit Service 82fcde
    abort ();
Packit Service 82fcde
Packit Service 82fcde
  /* Instead of the traditional loop which tests each character,
Packit Service 82fcde
     we will test a longword at a time.  The tricky part is testing
Packit Service 82fcde
     if *any of the four* bytes in the longword in question are zero.  */
Packit Service 82fcde
  while (longword_ptr < (unsigned long int *) end_ptr)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* We tentatively exit the loop if adding MAGIC_BITS to
Packit Service 82fcde
	 LONGWORD fails to change any of the hole bits of LONGWORD.
Packit Service 82fcde
Packit Service 82fcde
	 1) Is this safe?  Will it catch all the zero bytes?
Packit Service 82fcde
	 Suppose there is a byte with all zeros.  Any carry bits
Packit Service 82fcde
	 propagating from its left will fall into the hole at its
Packit Service 82fcde
	 least significant bit and stop.  Since there will be no
Packit Service 82fcde
	 carry from its most significant bit, the LSB of the
Packit Service 82fcde
	 byte to the left will be unchanged, and the zero will be
Packit Service 82fcde
	 detected.
Packit Service 82fcde
Packit Service 82fcde
	 2) Is this worthwhile?  Will it ignore everything except
Packit Service 82fcde
	 zero bytes?  Suppose every byte of LONGWORD has a bit set
Packit Service 82fcde
	 somewhere.  There will be a carry into bit 8.  If bit 8
Packit Service 82fcde
	 is set, this will carry into bit 16.  If bit 8 is clear,
Packit Service 82fcde
	 one of bits 9-15 must be set, so there will be a carry
Packit Service 82fcde
	 into bit 16.  Similarly, there will be a carry into bit
Packit Service 82fcde
	 24.  If one of bits 24-30 is set, there will be a carry
Packit Service 82fcde
	 into bit 31, so all of the hole bits will be changed.
Packit Service 82fcde
Packit Service 82fcde
	 The one misfire occurs when bits 24-30 are clear and bit
Packit Service 82fcde
	 31 is set; in this case, the hole at bit 31 is not
Packit Service 82fcde
	 changed.  If we had access to the processor carry flag,
Packit Service 82fcde
	 we could close this loophole by putting the fourth hole
Packit Service 82fcde
	 at bit 32!
Packit Service 82fcde
Packit Service 82fcde
	 So it ignores everything except 128's, when they're aligned
Packit Service 82fcde
	 properly.  */
Packit Service 82fcde
Packit Service 82fcde
      longword = *longword_ptr++;
Packit Service 82fcde
Packit Service 82fcde
      if ((longword - lomagic) & himagic)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Which of the bytes was the zero?  If none of them were, it was
Packit Service 82fcde
	     a misfire; continue the search.  */
Packit Service 82fcde
Packit Service 82fcde
	  const char *cp = (const char *) (longword_ptr - 1);
Packit Service 82fcde
Packit Service 82fcde
	  char_ptr = cp;
Packit Service 82fcde
	  if (cp[0] == 0)
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  char_ptr = cp + 1;
Packit Service 82fcde
	  if (cp[1] == 0)
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  char_ptr = cp + 2;
Packit Service 82fcde
	  if (cp[2] == 0)
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  char_ptr = cp + 3;
Packit Service 82fcde
	  if (cp[3] == 0)
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  if (sizeof (longword) > 4)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      char_ptr = cp + 4;
Packit Service 82fcde
	      if (cp[4] == 0)
Packit Service 82fcde
		break;
Packit Service 82fcde
	      char_ptr = cp + 5;
Packit Service 82fcde
	      if (cp[5] == 0)
Packit Service 82fcde
		break;
Packit Service 82fcde
	      char_ptr = cp + 6;
Packit Service 82fcde
	      if (cp[6] == 0)
Packit Service 82fcde
		break;
Packit Service 82fcde
	      char_ptr = cp + 7;
Packit Service 82fcde
	      if (cp[7] == 0)
Packit Service 82fcde
		break;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      char_ptr = end_ptr;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (char_ptr > end_ptr)
Packit Service 82fcde
    char_ptr = end_ptr;
Packit Service 82fcde
  return char_ptr - str;
Packit Service 82fcde
}
Packit Service 82fcde
#ifndef STRNLEN
Packit Service 82fcde
libc_hidden_def (__strnlen)
Packit Service 82fcde
weak_alias (__strnlen, strnlen)
Packit Service 82fcde
#endif
Packit Service 82fcde
libc_hidden_def (strnlen)