usearch 2.8.16 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
- package/.github/workflows/prerelease.yml +33 -7
- package/.github/workflows/release.yml +25 -26
- package/.gitmodules +4 -1
- package/CITATION.cff +1 -1
- package/CMakeLists.txt +6 -8
- package/CONTRIBUTING.md +46 -1
- package/Cargo.toml +1 -1
- package/README.md +7 -6
- package/VERSION +1 -1
- package/c/lib.cpp +19 -17
- package/c/test.c +41 -7
- package/c/usearch.h +15 -15
- package/conanfile.py +1 -1
- package/cpp/test.cpp +9 -2
- package/include/usearch/index.hpp +15 -15
- package/include/usearch/index_dense.hpp +8 -4
- package/include/usearch/index_plugins.hpp +34 -23
- package/javascript/usearch.js +1 -1
- package/package.json +3 -2
- package/pyproject.toml +2 -2
- package/setup.py +10 -4
- package/simsimd/.releaserc +3 -2
- package/simsimd/Cargo.lock +708 -0
- package/simsimd/Cargo.toml +36 -0
- package/simsimd/README.md +37 -1
- package/simsimd/build.rs +20 -0
- package/simsimd/include/simsimd/simsimd.h +4 -4
- package/simsimd/include/simsimd/spatial.h +1 -1
- package/simsimd/include/simsimd/types.h +5 -1
- package/simsimd/package.json +1 -1
- package/simsimd/rust/benches/cosine.rs +27 -0
- package/simsimd/rust/benches/native.rs +27 -0
- package/simsimd/rust/benches/sqeuclidean.rs +27 -0
- package/simsimd/rust/lib.c +68 -0
- package/simsimd/rust/lib.rs +232 -0
- package/sqlite/README.md +178 -0
- package/sqlite/lib.cpp +325 -0
- package/stringzilla/CONTRIBUTING.md +429 -0
- package/stringzilla/Cargo.lock +25 -0
- package/stringzilla/Cargo.toml +26 -0
- package/stringzilla/LICENSE +201 -0
- package/stringzilla/README.md +1281 -0
- package/stringzilla/VERSION +1 -0
- package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
- package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
- package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
- package/stringzilla/binding.gyp +16 -0
- package/stringzilla/build.rs +14 -0
- package/stringzilla/c/lib.c +304 -0
- package/stringzilla/cli/README.md +48 -0
- package/stringzilla/cli/split.py +72 -0
- package/stringzilla/cli/wc.py +37 -0
- package/stringzilla/include/stringzilla/experimental.h +724 -0
- package/stringzilla/include/stringzilla/module.modulemap +9 -0
- package/stringzilla/include/stringzilla/stringzilla.h +5240 -0
- package/stringzilla/include/stringzilla/stringzilla.hpp +3830 -0
- package/stringzilla/javascript/lib.c +125 -0
- package/stringzilla/javascript/stringzilla.js +22 -0
- package/stringzilla/package-ci.json +10 -0
- package/stringzilla/package.json +32 -0
- package/stringzilla/rust/lib.rs +362 -0
- package/stringzilla/swift/StringProtocol+StringZilla.swift +280 -0
- package/stringzilla/swift/Test.swift +60 -0
- package/wasmer.toml +1 -1
- package/simsimd/bench.js +0 -51
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file lib.c
|
|
3
|
+
* @brief JavaScript bindings for StringZilla.
|
|
4
|
+
* @author Ash Vardanian
|
|
5
|
+
* @date September 18, 2023
|
|
6
|
+
*
|
|
7
|
+
* @copyright Copyright (c) 2023
|
|
8
|
+
* @see NodeJS docs: https://nodejs.org/api/n-api.html
|
|
9
|
+
*/
|
|
10
|
+
#include <stdio.h> // `printf` for debug builds
|
|
11
|
+
#include <stdlib.h> // `malloc` to export strings into UTF-8
|
|
12
|
+
|
|
13
|
+
#include <node_api.h> // `napi_*` functions
|
|
14
|
+
|
|
15
|
+
#include <stringzilla/stringzilla.h> // `sz_*` functions
|
|
16
|
+
|
|
17
|
+
napi_value indexOfAPI(napi_env env, napi_callback_info info) {
|
|
18
|
+
size_t argc = 2;
|
|
19
|
+
napi_value args[2];
|
|
20
|
+
napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
21
|
+
|
|
22
|
+
// Extract the C string from the JavaScript string for haystack and needle
|
|
23
|
+
sz_string_view_t haystack = {NULL, 0};
|
|
24
|
+
sz_string_view_t needle = {NULL, 0};
|
|
25
|
+
|
|
26
|
+
// For haystack
|
|
27
|
+
napi_get_value_string_utf8(env, args[0], NULL, 0, (size_t *)&haystack.length);
|
|
28
|
+
haystack.start = malloc(haystack.length + 1);
|
|
29
|
+
napi_get_value_string_utf8(env, args[0], (char *)haystack.start, haystack.length + 1, (size_t *)&haystack.length);
|
|
30
|
+
|
|
31
|
+
// For needle
|
|
32
|
+
napi_get_value_string_utf8(env, args[1], NULL, 0, (size_t *)&needle.length);
|
|
33
|
+
needle.start = malloc(needle.length + 1);
|
|
34
|
+
napi_get_value_string_utf8(env, args[1], (char *)needle.start, needle.length + 1, (size_t *)&needle.length);
|
|
35
|
+
|
|
36
|
+
// Convert the result to JavaScript BigInt and return
|
|
37
|
+
napi_value js_result;
|
|
38
|
+
if (needle.length == 0) { napi_create_bigint_int64(env, 0, &js_result); }
|
|
39
|
+
else {
|
|
40
|
+
sz_cptr_t result = sz_find(haystack.start, haystack.length, needle.start, needle.length);
|
|
41
|
+
|
|
42
|
+
// In JavaScript, if `indexOf` is unable to indexOf the specified value, then it should return -1
|
|
43
|
+
if (result == NULL) { napi_create_bigint_int64(env, -1, &js_result); }
|
|
44
|
+
else { napi_create_bigint_uint64(env, result - haystack.start, &js_result); }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Cleanup
|
|
48
|
+
free((void *)haystack.start);
|
|
49
|
+
free((void *)needle.start);
|
|
50
|
+
return js_result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
napi_value countAPI(napi_env env, napi_callback_info info) {
|
|
54
|
+
size_t argc = 3;
|
|
55
|
+
napi_value args[3];
|
|
56
|
+
napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
57
|
+
|
|
58
|
+
// Extract the C string from the JavaScript string for haystack and needle
|
|
59
|
+
sz_string_view_t haystack = {NULL, 0};
|
|
60
|
+
sz_string_view_t needle = {NULL, 0};
|
|
61
|
+
|
|
62
|
+
// For haystack
|
|
63
|
+
napi_get_value_string_utf8(env, args[0], NULL, 0, (size_t *)&haystack.length);
|
|
64
|
+
haystack.start = malloc(haystack.length + 1);
|
|
65
|
+
napi_get_value_string_utf8(env, args[0], (char *)haystack.start, haystack.length + 1, (size_t *)&haystack.length);
|
|
66
|
+
|
|
67
|
+
// For needle
|
|
68
|
+
napi_get_value_string_utf8(env, args[1], NULL, 0, (size_t *)&needle.length);
|
|
69
|
+
needle.start = malloc(needle.length + 1);
|
|
70
|
+
napi_get_value_string_utf8(env, args[1], (char *)needle.start, needle.length + 1, (size_t *)&needle.length);
|
|
71
|
+
|
|
72
|
+
bool overlap = false;
|
|
73
|
+
if (argc > 2) { napi_get_value_bool(env, args[2], &overlap); }
|
|
74
|
+
|
|
75
|
+
void const *haystack_start = haystack.start, *needle_start = needle.start;
|
|
76
|
+
|
|
77
|
+
size_t count = 0;
|
|
78
|
+
if (needle.length == 0 || haystack.length == 0 || haystack.length < needle.length) { count = 0; }
|
|
79
|
+
else if (overlap) {
|
|
80
|
+
while (haystack.length) {
|
|
81
|
+
sz_cptr_t ptr = sz_find(haystack.start, haystack.length, needle.start, needle.length);
|
|
82
|
+
sz_bool_t found = ptr != NULL;
|
|
83
|
+
sz_size_t offset = found ? (sz_size_t)(ptr - haystack.start) : haystack.length;
|
|
84
|
+
count += found;
|
|
85
|
+
haystack.start += offset + found;
|
|
86
|
+
haystack.length -= offset + found;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
while (haystack.length) {
|
|
91
|
+
sz_cptr_t ptr = sz_find(haystack.start, haystack.length, needle.start, needle.length);
|
|
92
|
+
sz_bool_t found = ptr != NULL;
|
|
93
|
+
sz_size_t offset = found ? (sz_size_t)(ptr - haystack.start) : haystack.length;
|
|
94
|
+
count += found;
|
|
95
|
+
haystack.start += offset + needle.length;
|
|
96
|
+
haystack.length -= offset + needle.length * found;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Cleanup
|
|
101
|
+
free((void *)haystack_start);
|
|
102
|
+
free((void *)needle_start);
|
|
103
|
+
|
|
104
|
+
// Convert the `count` to JavaScript `BigInt` and return
|
|
105
|
+
napi_value js_count;
|
|
106
|
+
napi_create_bigint_uint64(env, count, &js_count);
|
|
107
|
+
|
|
108
|
+
return js_count;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
napi_value Init(napi_env env, napi_value exports) {
|
|
112
|
+
|
|
113
|
+
// Define an array of property descriptors
|
|
114
|
+
napi_property_descriptor findDesc = {"indexOf", 0, indexOfAPI, 0, 0, 0, napi_default, 0};
|
|
115
|
+
napi_property_descriptor countDesc = {"count", 0, countAPI, 0, 0, 0, napi_default, 0};
|
|
116
|
+
napi_property_descriptor properties[] = {findDesc, countDesc};
|
|
117
|
+
|
|
118
|
+
// Define the properties on the `exports` object
|
|
119
|
+
size_t propertyCount = sizeof(properties) / sizeof(properties[0]);
|
|
120
|
+
napi_define_properties(env, exports, propertyCount, properties);
|
|
121
|
+
|
|
122
|
+
return exports;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const compiled = require('bindings')('stringzilla');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
/**
|
|
5
|
+
* Searches for a short string in a long one.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} haystack
|
|
8
|
+
* @param {string} needle
|
|
9
|
+
* @returns {bigint}
|
|
10
|
+
*/
|
|
11
|
+
find: compiled.find,
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Searches for a substring in a larger string.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} haystack
|
|
17
|
+
* @param {string} needle
|
|
18
|
+
* @param {boolean} overlap
|
|
19
|
+
* @returns {bigint}
|
|
20
|
+
*/
|
|
21
|
+
count: compiled.count
|
|
22
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stringzilla",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "SIMD-accelerated string search, sort, hashes, fingerprints, & edit distances",
|
|
5
|
+
"author": "Ash Vardanian",
|
|
6
|
+
"license": "Apache 2.0",
|
|
7
|
+
"main": "javascript/stringzilla.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/ashvardanian/stringzilla.git"
|
|
12
|
+
},
|
|
13
|
+
"gypfile": true,
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": "~10 >=10.20 || >=12.17"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@types/node": "^20.4.5",
|
|
19
|
+
"bindings": "~1.2.1",
|
|
20
|
+
"node-addon-api": "^3.0.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "node --test ./scripts/test.js"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@semantic-release/exec": "^6.0.3",
|
|
27
|
+
"@semantic-release/git": "^10.0.1",
|
|
28
|
+
"conventional-changelog-eslint": "^3.0.9",
|
|
29
|
+
"semantic-release": "^21.1.2",
|
|
30
|
+
"typescript": "^5.1.6"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#![cfg_attr(not(test), no_std)]
|
|
2
|
+
|
|
3
|
+
use core::ffi::c_void;
|
|
4
|
+
|
|
5
|
+
// Import the functions from the StringZilla C library.
|
|
6
|
+
extern "C" {
|
|
7
|
+
fn sz_find(
|
|
8
|
+
haystack: *const c_void,
|
|
9
|
+
haystack_length: usize,
|
|
10
|
+
needle: *const c_void,
|
|
11
|
+
needle_length: usize,
|
|
12
|
+
) -> *const c_void;
|
|
13
|
+
|
|
14
|
+
fn sz_rfind(
|
|
15
|
+
haystack: *const c_void,
|
|
16
|
+
haystack_length: usize,
|
|
17
|
+
needle: *const c_void,
|
|
18
|
+
needle_length: usize,
|
|
19
|
+
) -> *const c_void;
|
|
20
|
+
|
|
21
|
+
fn sz_find_char_from(
|
|
22
|
+
haystack: *const c_void,
|
|
23
|
+
haystack_length: usize,
|
|
24
|
+
needle: *const c_void,
|
|
25
|
+
needle_length: usize,
|
|
26
|
+
) -> *const c_void;
|
|
27
|
+
|
|
28
|
+
fn sz_rfind_char_from(
|
|
29
|
+
haystack: *const c_void,
|
|
30
|
+
haystack_length: usize,
|
|
31
|
+
needle: *const c_void,
|
|
32
|
+
needle_length: usize,
|
|
33
|
+
) -> *const c_void;
|
|
34
|
+
|
|
35
|
+
fn sz_find_char_not_from(
|
|
36
|
+
haystack: *const c_void,
|
|
37
|
+
haystack_length: usize,
|
|
38
|
+
needle: *const c_void,
|
|
39
|
+
needle_length: usize,
|
|
40
|
+
) -> *const c_void;
|
|
41
|
+
|
|
42
|
+
fn sz_rfind_char_not_from(
|
|
43
|
+
haystack: *const c_void,
|
|
44
|
+
haystack_length: usize,
|
|
45
|
+
needle: *const c_void,
|
|
46
|
+
needle_length: usize,
|
|
47
|
+
) -> *const c_void;
|
|
48
|
+
|
|
49
|
+
fn sz_edit_distance(
|
|
50
|
+
haystack1: *const c_void,
|
|
51
|
+
haystack1_length: usize,
|
|
52
|
+
haystack2: *const c_void,
|
|
53
|
+
haystack2_length: usize,
|
|
54
|
+
bound: usize,
|
|
55
|
+
allocator: *const c_void,
|
|
56
|
+
) -> usize;
|
|
57
|
+
|
|
58
|
+
fn sz_alignment_score(
|
|
59
|
+
haystack1: *const c_void,
|
|
60
|
+
haystack1_length: usize,
|
|
61
|
+
haystack2: *const c_void,
|
|
62
|
+
haystack2_length: usize,
|
|
63
|
+
matrix: *const c_void,
|
|
64
|
+
gap: i8,
|
|
65
|
+
allocator: *const c_void,
|
|
66
|
+
) -> isize;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// The [StringZilla] trait provides a collection of string searching and manipulation functionalities.
|
|
70
|
+
pub trait StringZilla<N>
|
|
71
|
+
where
|
|
72
|
+
N: AsRef<[u8]>,
|
|
73
|
+
{
|
|
74
|
+
/// Locates first matching substring. Equivalent to `memmem(haystack, h_length, needle, n_length)` in LibC. Similar
|
|
75
|
+
/// to `strstr(haystack, needle)` in LibC, but requires known length.
|
|
76
|
+
fn sz_find(&self, needle: N) -> Option<usize>;
|
|
77
|
+
|
|
78
|
+
/// Locates the last matching substring.
|
|
79
|
+
fn sz_rfind(&self, needle: N) -> Option<usize>;
|
|
80
|
+
|
|
81
|
+
/// Finds the first character in the haystack, that is present in the needle.
|
|
82
|
+
fn sz_find_char_from(&self, needles: N) -> Option<usize>;
|
|
83
|
+
|
|
84
|
+
/// Finds the last character in the haystack, that is present in the needle.
|
|
85
|
+
fn sz_rfind_char_from(&self, needles: N) -> Option<usize>;
|
|
86
|
+
|
|
87
|
+
/// Finds the first character in the haystack, that is __not__ present in the needle.
|
|
88
|
+
fn sz_find_char_not_from(&self, needles: N) -> Option<usize>;
|
|
89
|
+
|
|
90
|
+
/// Finds the last character in the haystack, that is __not__ present in the needle.
|
|
91
|
+
fn sz_rfind_char_not_from(&self, needles: N) -> Option<usize>;
|
|
92
|
+
|
|
93
|
+
/// Computes the Levenshtein edit-distance between two strings using the Wagner-Fisher algorithm.
|
|
94
|
+
/// Similar to the Needleman-Wunsch alignment algorithm. Often used in fuzzy string matching.
|
|
95
|
+
fn sz_edit_distance(&self, needle: N) -> usize;
|
|
96
|
+
|
|
97
|
+
/// Computes Needleman–Wunsch alignment score for two strings. Often used in bioinformatics and cheminformatics.
|
|
98
|
+
/// Similar to the Levenshtein edit-distance, parameterized for gap and substitution penalties.
|
|
99
|
+
fn sz_alignment_score(&self, needle: N, matrix: [[i8; 256]; 256], gap: i8) -> isize;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
impl<T, N> StringZilla<N> for T
|
|
103
|
+
where
|
|
104
|
+
T: AsRef<[u8]>,
|
|
105
|
+
N: AsRef<[u8]>,
|
|
106
|
+
{
|
|
107
|
+
fn sz_find(&self, needle: N) -> Option<usize> {
|
|
108
|
+
let haystack_ref = self.as_ref();
|
|
109
|
+
let needle_ref = needle.as_ref();
|
|
110
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
111
|
+
let haystack_length = haystack_ref.len();
|
|
112
|
+
let needle_pointer = needle_ref.as_ptr() as _;
|
|
113
|
+
let needle_length = needle_ref.len();
|
|
114
|
+
let result = unsafe {
|
|
115
|
+
sz_find(
|
|
116
|
+
haystack_pointer,
|
|
117
|
+
haystack_length,
|
|
118
|
+
needle_pointer,
|
|
119
|
+
needle_length,
|
|
120
|
+
)
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if result.is_null() {
|
|
124
|
+
None
|
|
125
|
+
} else {
|
|
126
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn sz_rfind(&self, needle: N) -> Option<usize> {
|
|
131
|
+
let haystack_ref = self.as_ref();
|
|
132
|
+
let needle_ref = needle.as_ref();
|
|
133
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
134
|
+
let haystack_length = haystack_ref.len();
|
|
135
|
+
let needle_pointer = needle_ref.as_ptr() as _;
|
|
136
|
+
let needle_length = needle_ref.len();
|
|
137
|
+
let result = unsafe {
|
|
138
|
+
sz_rfind(
|
|
139
|
+
haystack_pointer,
|
|
140
|
+
haystack_length,
|
|
141
|
+
needle_pointer,
|
|
142
|
+
needle_length,
|
|
143
|
+
)
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if result.is_null() {
|
|
147
|
+
None
|
|
148
|
+
} else {
|
|
149
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn sz_find_char_from(&self, needles: N) -> Option<usize> {
|
|
154
|
+
let haystack_ref = self.as_ref();
|
|
155
|
+
let needles_ref = needles.as_ref();
|
|
156
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
157
|
+
let haystack_length = haystack_ref.len();
|
|
158
|
+
let needles_pointer = needles_ref.as_ptr() as _;
|
|
159
|
+
let needles_length = needles_ref.len();
|
|
160
|
+
let result = unsafe {
|
|
161
|
+
sz_find_char_from(
|
|
162
|
+
haystack_pointer,
|
|
163
|
+
haystack_length,
|
|
164
|
+
needles_pointer,
|
|
165
|
+
needles_length,
|
|
166
|
+
)
|
|
167
|
+
};
|
|
168
|
+
if result.is_null() {
|
|
169
|
+
None
|
|
170
|
+
} else {
|
|
171
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
fn sz_rfind_char_from(&self, needles: N) -> Option<usize> {
|
|
176
|
+
let haystack_ref = self.as_ref();
|
|
177
|
+
let needles_ref = needles.as_ref();
|
|
178
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
179
|
+
let haystack_length = haystack_ref.len();
|
|
180
|
+
let needles_pointer = needles_ref.as_ptr() as _;
|
|
181
|
+
let needles_length = needles_ref.len();
|
|
182
|
+
let result = unsafe {
|
|
183
|
+
sz_rfind_char_from(
|
|
184
|
+
haystack_pointer,
|
|
185
|
+
haystack_length,
|
|
186
|
+
needles_pointer,
|
|
187
|
+
needles_length,
|
|
188
|
+
)
|
|
189
|
+
};
|
|
190
|
+
if result.is_null() {
|
|
191
|
+
None
|
|
192
|
+
} else {
|
|
193
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn sz_find_char_not_from(&self, needles: N) -> Option<usize> {
|
|
198
|
+
let haystack_ref = self.as_ref();
|
|
199
|
+
let needles_ref = needles.as_ref();
|
|
200
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
201
|
+
let haystack_length = haystack_ref.len();
|
|
202
|
+
let needles_pointer = needles_ref.as_ptr() as _;
|
|
203
|
+
let needles_length = needles_ref.len();
|
|
204
|
+
let result = unsafe {
|
|
205
|
+
sz_find_char_not_from(
|
|
206
|
+
haystack_pointer,
|
|
207
|
+
haystack_length,
|
|
208
|
+
needles_pointer,
|
|
209
|
+
needles_length,
|
|
210
|
+
)
|
|
211
|
+
};
|
|
212
|
+
if result.is_null() {
|
|
213
|
+
None
|
|
214
|
+
} else {
|
|
215
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fn sz_rfind_char_not_from(&self, needles: N) -> Option<usize> {
|
|
220
|
+
let haystack_ref = self.as_ref();
|
|
221
|
+
let needles_ref = needles.as_ref();
|
|
222
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
223
|
+
let haystack_length = haystack_ref.len();
|
|
224
|
+
let needles_pointer = needles_ref.as_ptr() as _;
|
|
225
|
+
let needles_length = needles_ref.len();
|
|
226
|
+
let result = unsafe {
|
|
227
|
+
sz_rfind_char_not_from(
|
|
228
|
+
haystack_pointer,
|
|
229
|
+
haystack_length,
|
|
230
|
+
needles_pointer,
|
|
231
|
+
needles_length,
|
|
232
|
+
)
|
|
233
|
+
};
|
|
234
|
+
if result.is_null() {
|
|
235
|
+
None
|
|
236
|
+
} else {
|
|
237
|
+
Some(unsafe { result.offset_from(haystack_pointer) } as usize)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fn sz_edit_distance(&self, needle: N) -> usize {
|
|
242
|
+
let haystack_ref = self.as_ref();
|
|
243
|
+
let needle_ref = needle.as_ref();
|
|
244
|
+
let haystack_length = haystack_ref.len();
|
|
245
|
+
let needle_length = needle_ref.len();
|
|
246
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
247
|
+
let needle_pointer = needle_ref.as_ptr() as _;
|
|
248
|
+
unsafe {
|
|
249
|
+
sz_edit_distance(
|
|
250
|
+
haystack_pointer,
|
|
251
|
+
haystack_length,
|
|
252
|
+
needle_pointer,
|
|
253
|
+
needle_length,
|
|
254
|
+
// Upper bound on the distance, that allows us to exit early. If zero is
|
|
255
|
+
// passed, the maximum possible distance will be equal to the length of
|
|
256
|
+
// the longer input.
|
|
257
|
+
0,
|
|
258
|
+
// Uses the default allocator
|
|
259
|
+
core::ptr::null(),
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fn sz_alignment_score(&self, needle: N, matrix: [[i8; 256]; 256], gap: i8) -> isize {
|
|
265
|
+
let haystack_ref = self.as_ref();
|
|
266
|
+
let needle_ref = needle.as_ref();
|
|
267
|
+
let haystack_length = haystack_ref.len();
|
|
268
|
+
let needle_length = needle_ref.len();
|
|
269
|
+
let haystack_pointer = haystack_ref.as_ptr() as _;
|
|
270
|
+
let needle_pointer = needle_ref.as_ptr() as _;
|
|
271
|
+
unsafe {
|
|
272
|
+
sz_alignment_score(
|
|
273
|
+
haystack_pointer,
|
|
274
|
+
haystack_length,
|
|
275
|
+
needle_pointer,
|
|
276
|
+
needle_length,
|
|
277
|
+
matrix.as_ptr() as _,
|
|
278
|
+
gap,
|
|
279
|
+
core::ptr::null(),
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[cfg(test)]
|
|
286
|
+
mod tests {
|
|
287
|
+
use std::borrow::Cow;
|
|
288
|
+
|
|
289
|
+
use crate::StringZilla;
|
|
290
|
+
|
|
291
|
+
fn unary_substitution_costs() -> [[i8; 256]; 256] {
|
|
292
|
+
let mut result = [[0; 256]; 256];
|
|
293
|
+
|
|
294
|
+
for i in 0..256 {
|
|
295
|
+
for j in 0..256 {
|
|
296
|
+
result[i][j] = if i == j { 0 } else { -1 };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
result
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
#[test]
|
|
304
|
+
fn levenshtein() {
|
|
305
|
+
assert_eq!("hello".sz_edit_distance("hell"), 1);
|
|
306
|
+
assert_eq!("hello".sz_edit_distance("hell"), 1);
|
|
307
|
+
assert_eq!("abc".sz_edit_distance(""), 3);
|
|
308
|
+
assert_eq!("abc".sz_edit_distance("ac"), 1);
|
|
309
|
+
assert_eq!("abc".sz_edit_distance("a_bc"), 1);
|
|
310
|
+
assert_eq!("abc".sz_edit_distance("adc"), 1);
|
|
311
|
+
assert_eq!("ggbuzgjux{}l".sz_edit_distance("gbuzgjux{}l"), 1);
|
|
312
|
+
assert_eq!("abcdefgABCDEFG".sz_edit_distance("ABCDEFGabcdefg"), 14);
|
|
313
|
+
assert_eq!("fitting".sz_edit_distance("kitty"), 4);
|
|
314
|
+
assert_eq!("smitten".sz_edit_distance("mitten"), 1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#[test]
|
|
318
|
+
fn needleman() {
|
|
319
|
+
let costs_vector = unary_substitution_costs();
|
|
320
|
+
assert_eq!("listen".sz_alignment_score("silent", costs_vector, -1), -4);
|
|
321
|
+
assert_eq!(
|
|
322
|
+
"abcdefgABCDEFG".sz_alignment_score("ABCDEFGabcdefg", costs_vector, -1),
|
|
323
|
+
-14
|
|
324
|
+
);
|
|
325
|
+
assert_eq!("hello".sz_alignment_score("hello", costs_vector, -1), 0);
|
|
326
|
+
assert_eq!("hello".sz_alignment_score("hell", costs_vector, -1), -1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#[test]
|
|
330
|
+
fn basics() {
|
|
331
|
+
let my_string: String = String::from("Hello, world!");
|
|
332
|
+
let my_str: &str = my_string.as_str();
|
|
333
|
+
let my_cow_str: Cow<'_, str> = Cow::from(&my_string);
|
|
334
|
+
|
|
335
|
+
// Use the generic function with a String
|
|
336
|
+
assert_eq!(my_string.sz_find("world"), Some(7));
|
|
337
|
+
assert_eq!(my_string.sz_rfind("world"), Some(7));
|
|
338
|
+
assert_eq!(my_string.sz_find_char_from("world"), Some(2));
|
|
339
|
+
assert_eq!(my_string.sz_rfind_char_from("world"), Some(11));
|
|
340
|
+
assert_eq!(my_string.sz_find_char_not_from("world"), Some(0));
|
|
341
|
+
assert_eq!(my_string.sz_rfind_char_not_from("world"), Some(12));
|
|
342
|
+
|
|
343
|
+
// Use the generic function with a &str
|
|
344
|
+
assert_eq!(my_str.sz_find("world"), Some(7));
|
|
345
|
+
assert_eq!(my_str.sz_find("world"), Some(7));
|
|
346
|
+
assert_eq!(my_str.sz_find_char_from("world"), Some(2));
|
|
347
|
+
assert_eq!(my_str.sz_rfind_char_from("world"), Some(11));
|
|
348
|
+
assert_eq!(my_str.sz_find_char_not_from("world"), Some(0));
|
|
349
|
+
assert_eq!(my_str.sz_rfind_char_not_from("world"), Some(12));
|
|
350
|
+
|
|
351
|
+
// Use the generic function with a Cow<'_, str>
|
|
352
|
+
assert_eq!(my_cow_str.as_ref().sz_find("world"), Some(7));
|
|
353
|
+
assert_eq!(my_cow_str.as_ref().sz_find("world"), Some(7));
|
|
354
|
+
assert_eq!(my_cow_str.as_ref().sz_find_char_from("world"), Some(2));
|
|
355
|
+
assert_eq!(my_cow_str.as_ref().sz_rfind_char_from("world"), Some(11));
|
|
356
|
+
assert_eq!(my_cow_str.as_ref().sz_find_char_not_from("world"), Some(0));
|
|
357
|
+
assert_eq!(
|
|
358
|
+
my_cow_str.as_ref().sz_rfind_char_not_from("world"),
|
|
359
|
+
Some(12)
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|