1229198Sed/*-
2229198Sed * Copyright (c) 2012 Ed Schouten <ed@FreeBSD.org>
3229198Sed * All rights reserved.
4229198Sed *
5229198Sed * Redistribution and use in source and binary forms, with or without
6229198Sed * modification, are permitted provided that the following conditions
7229198Sed * are met:
8229198Sed * 1. Redistributions of source code must retain the above copyright
9229198Sed *    notice, this list of conditions and the following disclaimer.
10229198Sed * 2. Redistributions in binary form must reproduce the above copyright
11229198Sed *    notice, this list of conditions and the following disclaimer in the
12229198Sed *    documentation and/or other materials provided with the distribution.
13229198Sed *
14229198Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15229198Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16229198Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17229198Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18229198Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19229198Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20229198Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21229198Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22229198Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23229198Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24229198Sed * SUCH DAMAGE.
25229198Sed */
26229198Sed
27229198Sed#include <sys/cdefs.h>
28229198Sed__FBSDID("$FreeBSD$");
29229198Sed
30229198Sed#include <sys/libkern.h>
31229198Sed#include <sys/limits.h>
32229198Sed#include <sys/param.h>
33229198Sed
34229198Sed/*
35229198Sed * memcchr(): find first character in buffer not matching `c'.
36229198Sed *
37229198Sed * This function performs the complement of memchr().  To provide decent
38229198Sed * performance, this function compares data from the buffer one word at
39229198Sed * a time.
40229198Sed *
41229198Sed * This code is inspired by libc's strlen(), written by Xin Li.
42229198Sed */
43229198Sed
44229198Sed#if LONG_BIT != 32 && LONG_BIT != 64
45229198Sed#error Unsupported word size
46229198Sed#endif
47229198Sed
48229198Sed#define	LONGPTR_MASK (sizeof(long) - 1)
49229198Sed
50229198Sed#define	TESTBYTE				\
51229198Sed	do {					\
52229198Sed		if (*p != (unsigned char)c)	\
53229198Sed			goto done;		\
54229198Sed		p++;				\
55229198Sed	} while (0)
56229198Sed
57229198Sedvoid *
58229198Sedmemcchr(const void *begin, int c, size_t n)
59229198Sed{
60229198Sed	const unsigned long *lp;
61229198Sed	const unsigned char *p, *end;
62229198Sed	unsigned long word;
63229198Sed
64229198Sed	/* Four or eight repetitions of `c'. */
65229198Sed	word = (unsigned char)c;
66229198Sed	word |= word << 8;
67229198Sed	word |= word << 16;
68229198Sed#if LONG_BIT >= 64
69229198Sed	word |= word << 32;
70229198Sed#endif
71229198Sed
72229198Sed	/* Don't perform memory I/O when passing a zero-length buffer. */
73229198Sed	if (n == 0)
74229198Sed		return (NULL);
75229198Sed
76229198Sed	/*
77229198Sed	 * First determine whether there is a character unequal to `c'
78229198Sed	 * in the first word.  As this word may contain bytes before
79229198Sed	 * `begin', we may execute this loop spuriously.
80229198Sed	 */
81229198Sed	lp = (const unsigned long *)((uintptr_t)begin & ~LONGPTR_MASK);
82229198Sed	end = (const unsigned char *)begin + n;
83229198Sed	if (*lp++ != word)
84229198Sed		for (p = begin; p < (const unsigned char *)lp;)
85229198Sed			TESTBYTE;
86229198Sed
87229198Sed	/* Now compare the data one word at a time. */
88229198Sed	for (; (const unsigned char *)lp < end; lp++) {
89229198Sed		if (*lp != word) {
90229198Sed			p = (const unsigned char *)lp;
91229198Sed			TESTBYTE;
92229198Sed			TESTBYTE;
93229198Sed			TESTBYTE;
94229198Sed#if LONG_BIT >= 64
95229198Sed			TESTBYTE;
96229198Sed			TESTBYTE;
97229198Sed			TESTBYTE;
98229198Sed			TESTBYTE;
99229198Sed#endif
100229198Sed			goto done;
101229198Sed		}
102229198Sed	}
103229198Sed
104229198Sed	return (NULL);
105229198Sed
106229198Seddone:
107229198Sed	/*
108229198Sed	 * If the end of the buffer is not word aligned, the previous
109229198Sed	 * loops may obtain an address that's beyond the end of the
110229198Sed	 * buffer.
111229198Sed	 */
112229198Sed	if (p < end)
113229198Sed		return (__DECONST(void *, p));
114229198Sed	return (NULL);
115229198Sed}
116