/**************************************************************************** * libs/libc/string/lib_memmem.c * * The MIT License (MIT) * * Copyright (c) 2014-2015 Tal Einat * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifdef CONFIG_ALLOW_MIT_COMPONENTS #define LONG_INT_N_BYTES sizeof(long) #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: memmem * * Description: * The memmem() function finds the start of the first occurrence of the * substring needle of length needlelen in the memory area haystack of * length haystacklen. * * Returned Value: * The memmem() function returns a pointer to the beginning of the * substring, or NULL if the substring is not found. * ****************************************************************************/ #undef memmem /* See mm/README.txt */ FAR void *memmem(FAR const void *haystack, size_t haystacklen, FAR const void *needle, size_t needlelen) { #ifdef CONFIG_ALLOW_MIT_COMPONENTS FAR const unsigned char *needle_ptr; FAR const unsigned char *haystack_ptr; int sums_diff; size_t compare_len; unsigned long last_needle_chars; unsigned long last_haystack_chars; unsigned int i; switch (needlelen) { case (0): /* empty needle */ return (FAR void *)haystack; break; case (1): /* special case for single-character needles */ return memchr(haystack, *((FAR unsigned char *)needle), haystacklen); break; } /* start searching through haystack only from the first occurence of * the first character of needle. */ haystack_ptr = (FAR const unsigned char *)memchr(haystack, *((FAR const unsigned char *)needle), haystacklen); if (!haystack_ptr) { /* the first character of needle isn't in haystack */ return NULL; } haystacklen -= (haystack_ptr - (FAR const unsigned char *)haystack); if (haystacklen < needlelen) { /* the remaining haystack is smaller than needle */ return NULL; } haystack = (FAR void *)haystack_ptr; if (needlelen > LONG_INT_N_BYTES + 1) { needle_ptr = (FAR const unsigned char *)needle; sums_diff = 0; for (i = needlelen - LONG_INT_N_BYTES; i > 0; --i) { sums_diff -= *needle_ptr++; sums_diff += *haystack_ptr++; } last_needle_chars = 0; last_haystack_chars = 0; for (i = LONG_INT_N_BYTES; i > 0; --i) { last_needle_chars <<= 8; last_needle_chars ^= *needle_ptr; last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr; sums_diff -= *needle_ptr++; sums_diff += *haystack_ptr++; } /* we will call memcmp() only once we know that the sums are equal * and that LONG_INT_N_BYTES last chars are equal, so it will be * enough to compare all but the last LONG_INT_N_BYTES + 1 * characters. */ compare_len = needlelen - (LONG_INT_N_BYTES + 1); /* At this point: * needle is at least two characters long * haystack is at least needlelen characters long (also at least two) * the first characters of needle and haystack are identical */ if (sums_diff == 0 && last_haystack_chars == last_needle_chars && memcmp(haystack, needle, compare_len) == 0) { return (FAR void *)haystack; } /* iterate through the remainder of haystack, updating the sums' * differenceand checking for identity whenever the difference * is zero. */ for (i = haystacklen - needlelen; i > 0; --i) { last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr; sums_diff -= *(FAR const unsigned char *)haystack++; sums_diff += *haystack_ptr++; /* if sums_diff == 0, we know that the sums are equal, so it is * enough to compare all but the last characters. */ if (sums_diff == 0 && last_haystack_chars == last_needle_chars && memcmp(haystack, needle, compare_len) == 0) { return (FAR void *)haystack; } } } else if (needlelen < LONG_INT_N_BYTES) { needle_ptr = (FAR const unsigned char *)needle; sums_diff = 0; for (i = needlelen; i > 0; --i) { sums_diff -= *needle_ptr++; sums_diff += *haystack_ptr++; } /* we will call memcmp() only once we know that the sums are equal, * so it will be enough to compare all but the last characters. */ compare_len = needlelen - 1; /* At this point: * needle is at least two characters long * haystack is at least needlelen characters long (also at least two) * the first characters of needle and haystack are identical */ if (sums_diff == 0 && memcmp(haystack, needle, compare_len) == 0) { return (FAR void *)haystack; } /* iterate through the remainder of haystack, updating the sums' * difference and checking for identity whenever the difference * is zero. */ for (i = haystacklen - needlelen; i > 0; --i) { sums_diff -= *(FAR const unsigned char *)haystack++; sums_diff += *haystack_ptr++; /* if sums_diff == 0, we know that the sums are equal, so it is * enough to compare all but the last characters. */ if (sums_diff == 0 && memcmp(haystack, needle, compare_len) == 0) { return (FAR void *)haystack; } } } else if (needlelen == LONG_INT_N_BYTES) { needle_ptr = (FAR const unsigned char *)needle; last_needle_chars = 0; last_haystack_chars = 0; for (i = needlelen; i > 0; --i) { last_needle_chars <<= 8; last_needle_chars ^= *needle_ptr++; last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr++; } if (last_haystack_chars == last_needle_chars) { return (FAR void *)haystack; } /* iterate through the remainder of haystack, updating the last char * data and checking for equality. */ for (i = haystacklen - needlelen; i > 0; --i) { last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr++; if (last_haystack_chars == last_needle_chars) { return (FAR void *)(haystack_ptr - needlelen); } } } else /* needlelen == LONG_INT_N_BYTES + 1 */ { needle_ptr = (FAR const unsigned char *)needle; last_needle_chars = 0; last_haystack_chars = 0; for (i = LONG_INT_N_BYTES; i > 0; --i) { last_needle_chars <<= 8; last_needle_chars ^= *needle_ptr++; last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr++; } unsigned char last_needle_char = *(((FAR const unsigned char *)needle) + LONG_INT_N_BYTES); if (last_haystack_chars == last_needle_chars && *haystack_ptr == last_needle_char) { return (FAR void *)haystack; } /* iterate through the remainder of haystack, updating the last char * data and checking for equality. */ for (i = haystacklen - needlelen; i > 0; --i) { last_haystack_chars <<= 8; last_haystack_chars ^= *haystack_ptr++; if (last_haystack_chars == last_needle_chars && *haystack_ptr == last_needle_char) { return (FAR void *)(haystack_ptr - (needlelen - 1)); } } } #else FAR const unsigned char *h = haystack; FAR const unsigned char *n = needle; size_t i; size_t y; if (needlelen == 0) { return (FAR void *)haystack; } if (needlelen > haystacklen) { return NULL; } for (i = 0; i <= haystacklen - needlelen; i++) { y = 0; while (h[i + y] == n[y]) { if (++y == needlelen) { return (FAR void *)(h + i); } } } #endif return NULL; }