188 lines
5.8 KiB
C
188 lines
5.8 KiB
C
/* Optimized memmem function.
|
|
Copyright (c) 2018 Arm Ltd. All rights reserved.
|
|
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
3. The name of the company may not be used to endorse or promote
|
|
products derived from this software without specific prior written
|
|
permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
|
|
/*
|
|
FUNCTION
|
|
<<memmem>>---find memory segment
|
|
|
|
INDEX
|
|
memmem
|
|
|
|
SYNOPSIS
|
|
#include <string.h>
|
|
void *memmem(const void *<[s1]>, size_t <[l1]>, const void *<[s2]>,
|
|
size_t <[l2]>);
|
|
|
|
DESCRIPTION
|
|
|
|
Locates the first occurrence in the memory region pointed to
|
|
by <[s1]> with length <[l1]> of the sequence of bytes pointed
|
|
to by <[s2]> of length <[l2]>. If you already know the
|
|
lengths of your haystack and needle, <<memmem>> is much faster
|
|
than <<strstr>>.
|
|
|
|
RETURNS
|
|
Returns a pointer to the located segment, or a null pointer if
|
|
<[s2]> is not found. If <[l2]> is 0, <[s1]> is returned.
|
|
|
|
PORTABILITY
|
|
<<memmem>> is a newlib extension.
|
|
|
|
<<memmem>> requires no supporting OS subroutines.
|
|
|
|
QUICKREF
|
|
memmem pure
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
|
|
|
/* Small and efficient memmem implementation (quadratic worst-case). */
|
|
void *
|
|
memmem (const void *haystack, size_t hs_len, const void *needle, size_t ne_len)
|
|
{
|
|
const char *hs = haystack;
|
|
const char *ne = needle;
|
|
|
|
if (ne_len == 0)
|
|
return (void *)hs;
|
|
int i;
|
|
int c = ne[0];
|
|
const char *end = hs + hs_len - ne_len;
|
|
|
|
for ( ; hs <= end; hs++)
|
|
{
|
|
if (hs[0] != c)
|
|
continue;
|
|
for (i = ne_len - 1; i != 0; i--)
|
|
if (hs[i] != ne[i])
|
|
break;
|
|
if (i == 0)
|
|
return (void *)hs;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#else
|
|
|
|
# define RETURN_TYPE void *
|
|
# define AVAILABLE(h, h_l, j, n_l) ((j) <= (h_l) - (n_l))
|
|
# include "str-two-way.h"
|
|
|
|
#define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift))
|
|
|
|
/* Fast memmem algorithm with guaranteed linear-time performance.
|
|
Small needles up to size 2 use a dedicated linear search. Longer needles
|
|
up to size 256 use a novel modified Horspool algorithm. It hashes pairs
|
|
of characters to quickly skip past mismatches. The main search loop only
|
|
exits if the last 2 characters match, avoiding unnecessary calls to memcmp
|
|
and allowing for a larger skip if there is no match. A self-adapting
|
|
filtering check is used to quickly detect mismatches in long needles.
|
|
By limiting the needle length to 256, the shift table can be reduced to 8
|
|
bits per entry, lowering preprocessing overhead and minimizing cache effects.
|
|
The limit also implies worst-case performance is linear.
|
|
Needles larger than 256 characters use the linear-time Two-Way algorithm. */
|
|
void *
|
|
memmem (const void *haystack, size_t hs_len, const void *needle, size_t ne_len)
|
|
{
|
|
const unsigned char *hs = haystack;
|
|
const unsigned char *ne = needle;
|
|
|
|
if (ne_len == 0)
|
|
return (void *) hs;
|
|
if (ne_len == 1)
|
|
return (void *) memchr (hs, ne[0], hs_len);
|
|
|
|
/* Ensure haystack length is >= needle length. */
|
|
if (hs_len < ne_len)
|
|
return NULL;
|
|
|
|
const unsigned char *end = hs + hs_len - ne_len;
|
|
|
|
if (ne_len == 2)
|
|
{
|
|
uint32_t nw = ne[0] << 16 | ne[1], hw = hs[0] << 16 | hs[1];
|
|
for (hs++; hs <= end && hw != nw; )
|
|
hw = hw << 16 | *++hs;
|
|
return hw == nw ? (void *)(hs - 1) : NULL;
|
|
}
|
|
|
|
/* Use Two-Way algorithm for very long needles. */
|
|
if (__builtin_expect (ne_len > 256, 0))
|
|
return two_way_long_needle (hs, hs_len, ne, ne_len);
|
|
|
|
uint8_t shift[256];
|
|
size_t tmp, shift1;
|
|
size_t m1 = ne_len - 1;
|
|
size_t offset = 0;
|
|
int i;
|
|
|
|
/* Initialize bad character shift hash table. */
|
|
memset (shift, 0, sizeof (shift));
|
|
for (i = 1; i < m1; i++)
|
|
shift[hash2 (ne + i)] = i;
|
|
shift1 = m1 - shift[hash2 (ne + m1)];
|
|
shift[hash2 (ne + m1)] = m1;
|
|
|
|
for ( ; hs <= end; )
|
|
{
|
|
/* Skip past character pairs not in the needle. */
|
|
do
|
|
{
|
|
hs += m1;
|
|
tmp = shift[hash2 (hs)];
|
|
}
|
|
while (hs <= end && tmp == 0);
|
|
|
|
/* If the match is not at the end of the needle, shift to the end
|
|
and continue until we match the last 2 characters. */
|
|
hs -= tmp;
|
|
if (tmp < m1)
|
|
continue;
|
|
|
|
/* The last 2 characters match. If the needle is long, check a
|
|
fixed number of characters first to quickly filter out mismatches. */
|
|
if (m1 <= 15 || memcmp (hs + offset, ne + offset, sizeof (long)) == 0)
|
|
{
|
|
if (memcmp (hs, ne, m1) == 0)
|
|
return (void *) hs;
|
|
|
|
/* Adjust filter offset when it doesn't find the mismatch. */
|
|
offset = (offset >= sizeof (long) ? offset : m1) - sizeof (long);
|
|
}
|
|
|
|
/* Skip based on matching the last 2 characters. */
|
|
hs += shift1;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif /* Compilation for speed. */
|