From f77673252db2866bb7cca1f87518829ca433abe5 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 31 May 2024 22:28:17 +0300 Subject: [PATCH 1/9] initial commit --- Objects/stringlib/fastsearch.h | 39 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index 257b7bd6788ad2..40eeb7e661030e 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -337,21 +337,20 @@ STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, if (p->is_periodic) { assert(p->cut <= len_needle/2); assert(p->cut < p->period); - p->gap = 0; // unused } else { // A lower bound on the period p->period = Py_MAX(p->cut, len_needle - p->cut) + 1; - // The gap between the last character and the previous - // occurrence of an equivalent character (modulo TABLE_SIZE) - p->gap = len_needle; - STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK; - for (Py_ssize_t i = len_needle - 2; i >= 0; i--) { - STRINGLIB_CHAR x = needle[i] & TABLE_MASK; - if (x == last) { - p->gap = len_needle - 1 - i; - break; - } + } + // The gap between the last character and the previous + // occurrence of an equivalent character (modulo TABLE_SIZE) + p->gap = len_needle; + STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK; + for (Py_ssize_t i = len_needle - 2; i >= 0; i--) { + STRINGLIB_CHAR x = needle[i] & TABLE_MASK; + if (x == last) { + p->gap = len_needle - 1 - i; + break; } } // Fill up a compressed Boyer-Moore "Bad Character" table @@ -383,6 +382,8 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, const STRINGLIB_CHAR *window; LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack); + Py_ssize_t gap = p->gap; + Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap); if (p->is_periodic) { LOG("Needle is periodic.\n"); Py_ssize_t memory = 0; @@ -406,9 +407,19 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, assert((window[len_needle - 1] & TABLE_MASK) == (needle[len_needle - 1] & TABLE_MASK)); Py_ssize_t i = Py_MAX(cut, memory); - for (; i < len_needle; i++) { + for (; i < gap_jump_end; i++) { if (needle[i] != window[i]) { - LOG("Right half does not match.\n"); + LOG("Early right half mismatch: jump by gap.\n"); + assert(gap >= i - cut + 1); + window_last += gap; + memory = 0; + goto periodicwindowloop; + } + } + for (Py_ssize_t i = gap_jump_end; i < len_needle; i++) { + if (needle[i] != window[i]) { + LOG("Late right half mismatch.\n"); + assert(i - cut + 1 > gap); window_last += i - cut + 1; memory = 0; goto periodicwindowloop; @@ -442,10 +453,8 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, } } else { - Py_ssize_t gap = p->gap; period = Py_MAX(gap, period); LOG("Needle is not periodic.\n"); - Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap); windowloop: while (window_last < haystack_end) { for (;;) { From 01b0614d0fe376f0f586317ae9ddba5562b536da Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 31 May 2024 23:10:01 +0300 Subject: [PATCH 2/9] comment typo --- Objects/stringlib/fastsearch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index 40eeb7e661030e..1c0999eb9358d7 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -256,7 +256,7 @@ STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle, The local period of the cut is the minimal length of a string w such that (left endswith w or w endswith left) - and (right startswith w or w startswith left). + and (right startswith w or w startswith right). The Critical Factorization Theorem says that this maximal local period is the global period of the string. From 854a3967268bae04d45eeb633b03a4c8146944e4 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sat, 1 Jun 2024 23:49:19 +0300 Subject: [PATCH 3/9] simplified 2 loops into 1 --- Objects/stringlib/fastsearch.h | 46 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index 1c0999eb9358d7..deebca81b8bd34 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -406,21 +406,18 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, window = window_last - len_needle + 1; assert((window[len_needle - 1] & TABLE_MASK) == (needle[len_needle - 1] & TABLE_MASK)); + Py_ssize_t i = Py_MAX(cut, memory); - for (; i < gap_jump_end; i++) { - if (needle[i] != window[i]) { - LOG("Early right half mismatch: jump by gap.\n"); - assert(gap >= i - cut + 1); - window_last += gap; - memory = 0; - goto periodicwindowloop; - } - } - for (Py_ssize_t i = gap_jump_end; i < len_needle; i++) { + for (; i < len_needle; i++) { if (needle[i] != window[i]) { - LOG("Late right half mismatch.\n"); - assert(i - cut + 1 > gap); - window_last += i - cut + 1; + if (i < gap_jump_end) { + LOG("Early right half mismatch: jump by gap.\n"); + window_last += gap; + } + else { + LOG("Late right half mismatch: jump by n (>gap)\n"); + window_last += i - cut + 1; + } memory = 0; goto periodicwindowloop; } @@ -472,19 +469,18 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, window = window_last - len_needle + 1; assert((window[len_needle - 1] & TABLE_MASK) == (needle[len_needle - 1] & TABLE_MASK)); - for (Py_ssize_t i = cut; i < gap_jump_end; i++) { - if (needle[i] != window[i]) { - LOG("Early right half mismatch: jump by gap.\n"); - assert(gap >= i - cut + 1); - window_last += gap; - goto windowloop; - } - } - for (Py_ssize_t i = gap_jump_end; i < len_needle; i++) { + + Py_ssize_t i = cut; + for (; i < len_needle; i++) { if (needle[i] != window[i]) { - LOG("Late right half mismatch.\n"); - assert(i - cut + 1 > gap); - window_last += i - cut + 1; + if (i < gap_jump_end) { + LOG("Early right half mismatch: jump by gap.\n"); + window_last += gap; + } + else { + LOG("Late right half mismatch: jump by n (>gap)\n"); + window_last += i - cut + 1; + } goto windowloop; } } From 1308b0640c38ec469cafd093dc6d10ccde129252 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 2 Jun 2024 06:15:04 +0300 Subject: [PATCH 4/9] added back assertions --- Objects/stringlib/fastsearch.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index deebca81b8bd34..488793e3ce5d2c 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -412,10 +412,12 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, if (needle[i] != window[i]) { if (i < gap_jump_end) { LOG("Early right half mismatch: jump by gap.\n"); + assert(gap >= i - cut + 1); window_last += gap; } else { LOG("Late right half mismatch: jump by n (>gap)\n"); + assert(i - cut + 1 > gap); window_last += i - cut + 1; } memory = 0; @@ -475,10 +477,12 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, if (needle[i] != window[i]) { if (i < gap_jump_end) { LOG("Early right half mismatch: jump by gap.\n"); + assert(i < gap + cut); window_last += gap; } else { LOG("Late right half mismatch: jump by n (>gap)\n"); + assert(i >= gap + cut); window_last += i - cut + 1; } goto windowloop; From fafadb5afd65f64bbbf395f3347f0a06199b5192 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 2 Jun 2024 09:06:48 +0300 Subject: [PATCH 5/9] blank line removed --- Objects/stringlib/fastsearch.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index 488793e3ce5d2c..a512e15b17206a 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -471,7 +471,6 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, window = window_last - len_needle + 1; assert((window[len_needle - 1] & TABLE_MASK) == (needle[len_needle - 1] & TABLE_MASK)); - Py_ssize_t i = cut; for (; i < len_needle; i++) { if (needle[i] != window[i]) { From 486812c2ae9c576b6d91ccc6ada4bf7bc0d96d2b Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 06:12:36 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-06-02-06-12-35.gh-issue-119879.Jit951.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst new file mode 100644 index 00000000000000..07106a4652225d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst @@ -0,0 +1 @@ +Utilization of last character gap (good suffix rule) for two-way periodic needles. From 1fb518a09f7c912d3386d3e4e89920e7442b930b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 3 Jun 2024 03:07:29 +0300 Subject: [PATCH 7/9] revert assertion order --- Objects/stringlib/fastsearch.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index a512e15b17206a..48cf15f5468644 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -476,12 +476,12 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, if (needle[i] != window[i]) { if (i < gap_jump_end) { LOG("Early right half mismatch: jump by gap.\n"); - assert(i < gap + cut); + assert(gap >= i - cut + 1); window_last += gap; } else { LOG("Late right half mismatch: jump by n (>gap)\n"); - assert(i >= gap + cut); + assert(i - cut + 1 > gap); window_last += i - cut + 1; } goto windowloop; From 6fd794751d301169cf5eb8d4b866b5d44b5abd5b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 3 Jun 2024 21:58:29 +0300 Subject: [PATCH 8/9] better news --- .../2024-06-02-06-12-35.gh-issue-119879.Jit951.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst index 07106a4652225d..89de6b0299a35a 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-02-06-12-35.gh-issue-119879.Jit951.rst @@ -1 +1 @@ -Utilization of last character gap (good suffix rule) for two-way periodic needles. +String search is now slightly faster for certain cases. It now utilizes last character gap (good suffix rule) for two-way periodic needles. From 94ec8d743d5cb9dac6f8a316d9ff2f03d9d37edd Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Tue, 4 Jun 2024 10:13:44 +0300 Subject: [PATCH 9/9] whitespace --- Objects/stringlib/fastsearch.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index 48cf15f5468644..309ed1554f4699 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -406,7 +406,6 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, window = window_last - len_needle + 1; assert((window[len_needle - 1] & TABLE_MASK) == (needle[len_needle - 1] & TABLE_MASK)); - Py_ssize_t i = Py_MAX(cut, memory); for (; i < len_needle; i++) { if (needle[i] != window[i]) {