Skip to content

Commit c59d0c4

Browse files
committed
Optimize find/count with memchr+memcmp pattern for fast short-pattern search
1 parent d6a836c commit c59d0c4

1 file changed

Lines changed: 54 additions & 12 deletions

File tree

c_src/py_reactor_buffer.c

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
* @brief Zero-copy buffer implementation for reactor protocol layer
2020
*/
2121

22+
/* Enable memmem on Linux (it's a GNU extension) */
23+
#ifndef _GNU_SOURCE
24+
#define _GNU_SOURCE
25+
#endif
26+
2227
#include "py_reactor_buffer.h"
2328
#include <unistd.h>
2429
#include <errno.h>
@@ -393,20 +398,33 @@ static PyObject *ReactorBuffer_find(ReactorBufferObject *self, PyObject *args) {
393398

394399
Py_ssize_t result = -1;
395400
if (start <= end && sub_buf.len <= (end - start)) {
396-
/* Simple search - for small patterns memmem isn't always available */
397401
const unsigned char *haystack = self->resource->data + start;
398402
Py_ssize_t haystack_len = end - start;
399403
const unsigned char *needle = sub_buf.buf;
400404
Py_ssize_t needle_len = sub_buf.len;
401405

402406
if (needle_len == 0) {
403407
result = start;
408+
} else if (needle_len == 1) {
409+
/* Single byte: use memchr (very fast) */
410+
void *found = memchr(haystack, needle[0], haystack_len);
411+
if (found != NULL) {
412+
result = start + ((const unsigned char *)found - haystack);
413+
}
404414
} else {
405-
for (Py_ssize_t i = 0; i <= haystack_len - needle_len; i++) {
406-
if (memcmp(haystack + i, needle, needle_len) == 0) {
407-
result = start + i;
415+
/* Multi-byte: use memchr to find first byte, then memcmp to verify.
416+
* This is faster than memmem for short patterns (HTTP parsing). */
417+
const unsigned char *p = haystack;
418+
Py_ssize_t remaining = haystack_len;
419+
while (remaining >= needle_len) {
420+
p = memchr(p, needle[0], remaining);
421+
if (p == NULL) break;
422+
if (memcmp(p, needle, needle_len) == 0) {
423+
result = start + (p - haystack);
408424
break;
409425
}
426+
p++;
427+
remaining = haystack_len - (p - haystack);
410428
}
411429
}
412430
}
@@ -450,11 +468,16 @@ static PyObject *ReactorBuffer_rfind(ReactorBufferObject *self, PyObject *args)
450468
if (needle_len == 0) {
451469
result = end;
452470
} else {
453-
for (Py_ssize_t i = haystack_len - needle_len; i >= 0; i--) {
454-
if (memcmp(haystack + i, needle, needle_len) == 0) {
455-
result = start + i;
456-
break;
457-
}
471+
/* Use memmem iteratively to find last occurrence */
472+
const unsigned char *search_start = haystack;
473+
Py_ssize_t search_len = haystack_len;
474+
void *found;
475+
while ((found = memmem(search_start, search_len, needle, needle_len)) != NULL) {
476+
result = start + ((const unsigned char *)found - haystack);
477+
/* Continue searching after this match */
478+
search_start = (const unsigned char *)found + 1;
479+
search_len = haystack_len - (search_start - haystack);
480+
if (search_len < needle_len) break;
458481
}
459482
}
460483
}
@@ -512,10 +535,29 @@ static PyObject *ReactorBuffer_count(ReactorBufferObject *self, PyObject *args)
512535
const unsigned char *needle = sub_buf.buf;
513536
Py_ssize_t needle_len = sub_buf.len;
514537

515-
for (Py_ssize_t i = 0; i <= haystack_len - needle_len; i++) {
516-
if (memcmp(haystack + i, needle, needle_len) == 0) {
538+
if (needle_len == 1) {
539+
/* Single byte: use memchr repeatedly */
540+
const unsigned char *p = haystack;
541+
Py_ssize_t remaining = haystack_len;
542+
while ((p = memchr(p, needle[0], remaining)) != NULL) {
517543
count++;
518-
i += needle_len - 1; /* Non-overlapping */
544+
p++;
545+
remaining = haystack_len - (p - haystack);
546+
}
547+
} else {
548+
/* Multi-byte: use memchr + memcmp pattern */
549+
const unsigned char *p = haystack;
550+
Py_ssize_t remaining = haystack_len;
551+
while (remaining >= needle_len) {
552+
p = memchr(p, needle[0], remaining);
553+
if (p == NULL) break;
554+
if (memcmp(p, needle, needle_len) == 0) {
555+
count++;
556+
p += needle_len; /* Non-overlapping */
557+
} else {
558+
p++;
559+
}
560+
remaining = haystack_len - (p - haystack);
519561
}
520562
}
521563
} else if (sub_buf.len == 0 && start <= end) {

0 commit comments

Comments
 (0)