Pregunta:
Recientemente, estaba memmem
una aplicación que usa memmem
de Linux a Windows. Si bien memmem
está disponible como función de biblioteca en Linux, MSDN / Windows no lo ofrece. Podría haber usado strstr
pero luego tiene un prototipo muy diferente y solo espera cadenas terminadas en nulo como argumentos. Buscando en Google, descubrí algunas implementaciones de memmem
pero ninguna parecía imitar realmente memmem
.
Por lo tanto, escribí uno de los míos:
void *memmem(const void *haystack_start, size_t haystack_len, const void *needle_start, size_t needle_len)
{
const unsigned char *haystack = (const unsigned char *) haystack_start;
const unsigned char *needle = (const unsigned char *) needle_start;
const unsigned char *h = NULL;
const unsigned char *n = NULL;
size_t x = needle_len;
/* The first occurrence of the empty string is deemed to occur at
the beginning of the string. */
if (needle_len == 0)
return (void *) haystack_start;
/* Sanity check, otherwise the loop might search through the whole
memory. */
if (haystack_len < needle_len)
return NULL;
for (; *haystack && haystack_len--; haystack++) {
x = needle_len;
n = needle;
h = haystack;
if (haystack_len < needle_len)
break;
if ((*haystack != *needle) || ( *haystack + needle_len != *needle + needle_len))
continue;
for (; x ; h++ , n++) {
x--;
if (*h != *n)
break;
if (x == 0)
return (void *)haystack;
}
}
return NULL;
}
El código funciona, pero mi pregunta es: ¿podría hacerse de manera más eficiente o más rápida?
Espero que uno de los increíbles trucos aquí también tenga la amabilidad de señalar si esta implementación tiene una vulnerabilidad que quizás no haya notado.
Respuesta:
-
Tu escribiste:
for (; *haystack && haystack_len--; haystack++) { x = needle_len; n = needle; h = haystack; if (haystack_len < needle_len) break;
Una mejor forma sería:
for (; --haystack_len >= needle_len; haystack++) { x = needle_len; n = needle; h = haystack;
El
*haystack
es en realidad un error, ya que evita la búsqueda si hay cero bytes en el rango. -
Creo que es una mala idea tener la segunda cláusula en:
if ((*haystack != *needle) || ( *haystack + needle_len != *needle + needle_len))
porque podrías romper el caché. Mantenga el acceso a los datos local. Comprobar el último carácter es una complejidad innecesaria.