usearch 2.8.16 → 2.9.0
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 +20 -1
- package/Cargo.toml +1 -1
- package/README.md +7 -6
- package/VERSION +1 -1
- package/conanfile.py +1 -1
- package/cpp/test.cpp +9 -2
- package/include/usearch/index.hpp +11 -11
- package/include/usearch/index_plugins.hpp +2 -1
- package/javascript/usearch.js +1 -1
- package/package.json +1 -1
- 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 @@
|
|
|
1
|
+
3.2.0
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "stringzilla",
|
|
5
|
+
"sources": ["javascript/lib.c"],
|
|
6
|
+
"include_dirs": ["include"],
|
|
7
|
+
"cflags": [
|
|
8
|
+
"-std=c99",
|
|
9
|
+
"-Wno-unknown-pragmas",
|
|
10
|
+
"-Wno-maybe-uninitialized",
|
|
11
|
+
"-Wno-cast-function-type",
|
|
12
|
+
"-Wno-unused-function",
|
|
13
|
+
],
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
fn main() {
|
|
2
|
+
cc::Build::new()
|
|
3
|
+
.file("c/lib.c")
|
|
4
|
+
.include("include")
|
|
5
|
+
.warnings(false)
|
|
6
|
+
.flag_if_supported("-std=c99")
|
|
7
|
+
.flag_if_supported("-fcolor-diagnostics")
|
|
8
|
+
.flag_if_supported("-fPIC")
|
|
9
|
+
.compile("stringzilla");
|
|
10
|
+
|
|
11
|
+
println!("cargo:rerun-if-changed=c/lib.c");
|
|
12
|
+
println!("cargo:rerun-if-changed=rust/lib.rs");
|
|
13
|
+
println!("cargo:rerun-if-changed=include/stringzilla/stringzilla.h");
|
|
14
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file lib.c
|
|
3
|
+
* @brief StringZilla C library with dynamic backed dispatch for the most appropriate implementation.
|
|
4
|
+
* @author Ash Vardanian
|
|
5
|
+
* @date January 16, 2024
|
|
6
|
+
* @copyright Copyright (c) 2024
|
|
7
|
+
*/
|
|
8
|
+
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
9
|
+
#include <windows.h> // `DllMain`
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
// Overwrite `SZ_DYNAMIC_DISPATCH` before including StringZilla.
|
|
13
|
+
#ifdef SZ_DYNAMIC_DISPATCH
|
|
14
|
+
#undef SZ_DYNAMIC_DISPATCH
|
|
15
|
+
#endif
|
|
16
|
+
#define SZ_DYNAMIC_DISPATCH 1
|
|
17
|
+
#include <stringzilla/stringzilla.h>
|
|
18
|
+
|
|
19
|
+
#if SZ_AVOID_LIBC
|
|
20
|
+
// If we don't have the LibC, the `malloc` definition in `stringzilla.h` will be illformed.
|
|
21
|
+
#ifdef _MSC_VER
|
|
22
|
+
typedef sz_size_t size_t; // Reuse the type definition we've inferred from `stringzilla.h`
|
|
23
|
+
#else
|
|
24
|
+
typedef __SIZE_TYPE__ size_t; // For GCC/Clang
|
|
25
|
+
#endif
|
|
26
|
+
void free(void *start) { sz_unused(start); }
|
|
27
|
+
void *malloc(size_t length) {
|
|
28
|
+
sz_unused(length);
|
|
29
|
+
return SZ_NULL;
|
|
30
|
+
}
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
SZ_DYNAMIC sz_capability_t sz_capabilities(void) {
|
|
34
|
+
|
|
35
|
+
#if SZ_USE_X86_AVX512 || SZ_USE_X86_AVX2
|
|
36
|
+
|
|
37
|
+
/// The states of 4 registers populated for a specific "cpuid" assembly call
|
|
38
|
+
union four_registers_t {
|
|
39
|
+
int array[4];
|
|
40
|
+
struct separate_t {
|
|
41
|
+
unsigned eax, ebx, ecx, edx;
|
|
42
|
+
} named;
|
|
43
|
+
} info1, info7;
|
|
44
|
+
|
|
45
|
+
#ifdef _MSC_VER
|
|
46
|
+
__cpuidex(info1.array, 1, 0);
|
|
47
|
+
__cpuidex(info7.array, 7, 0);
|
|
48
|
+
#else
|
|
49
|
+
__asm__ __volatile__("cpuid"
|
|
50
|
+
: "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
|
|
51
|
+
: "a"(1), "c"(0));
|
|
52
|
+
__asm__ __volatile__("cpuid"
|
|
53
|
+
: "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
|
|
54
|
+
: "a"(7), "c"(0));
|
|
55
|
+
#endif
|
|
56
|
+
|
|
57
|
+
// Check for AVX2 (Function ID 7, EBX register)
|
|
58
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
|
|
59
|
+
unsigned supports_avx2 = (info7.named.ebx & 0x00000020) != 0;
|
|
60
|
+
// Check for AVX512F (Function ID 7, EBX register)
|
|
61
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
|
|
62
|
+
unsigned supports_avx512f = (info7.named.ebx & 0x00010000) != 0;
|
|
63
|
+
// Check for AVX512BW (Function ID 7, EBX register)
|
|
64
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L166
|
|
65
|
+
unsigned supports_avx512bw = (info7.named.ebx & 0x40000000) != 0;
|
|
66
|
+
// Check for AVX512VL (Function ID 7, EBX register)
|
|
67
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L167C25-L167C35
|
|
68
|
+
unsigned supports_avx512vl = (info7.named.ebx & 0x80000000) != 0;
|
|
69
|
+
// Check for GFNI (Function ID 1, ECX register)
|
|
70
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L171C30-L171C40
|
|
71
|
+
unsigned supports_avx512vbmi = (info1.named.ecx & 0x00000002) != 0;
|
|
72
|
+
// Check for GFNI (Function ID 1, ECX register)
|
|
73
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L177C30-L177C40
|
|
74
|
+
unsigned supports_gfni = (info1.named.ecx & 0x00000100) != 0;
|
|
75
|
+
|
|
76
|
+
return (sz_capability_t)( //
|
|
77
|
+
(sz_cap_x86_avx2_k * supports_avx2) | //
|
|
78
|
+
(sz_cap_x86_avx512f_k * supports_avx512f) | //
|
|
79
|
+
(sz_cap_x86_avx512vl_k * supports_avx512vl) | //
|
|
80
|
+
(sz_cap_x86_avx512bw_k * supports_avx512bw) | //
|
|
81
|
+
(sz_cap_x86_avx512vbmi_k * supports_avx512vbmi) | //
|
|
82
|
+
(sz_cap_x86_gfni_k * (supports_gfni)) | //
|
|
83
|
+
(sz_cap_serial_k));
|
|
84
|
+
|
|
85
|
+
#endif // SIMSIMD_TARGET_X86
|
|
86
|
+
|
|
87
|
+
#if SZ_USE_ARM_NEON || SZ_USE_ARM_SVE
|
|
88
|
+
|
|
89
|
+
// Every 64-bit Arm CPU supports NEON
|
|
90
|
+
unsigned supports_neon = 1;
|
|
91
|
+
unsigned supports_sve = 0;
|
|
92
|
+
unsigned supports_sve2 = 0;
|
|
93
|
+
sz_unused(supports_sve);
|
|
94
|
+
sz_unused(supports_sve2);
|
|
95
|
+
|
|
96
|
+
return (sz_capability_t)( //
|
|
97
|
+
(sz_cap_arm_neon_k * supports_neon) | //
|
|
98
|
+
(sz_cap_serial_k));
|
|
99
|
+
|
|
100
|
+
#endif // SIMSIMD_TARGET_ARM
|
|
101
|
+
|
|
102
|
+
return sz_cap_serial_k;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
typedef struct sz_implementations_t {
|
|
106
|
+
sz_equal_t equal;
|
|
107
|
+
sz_order_t order;
|
|
108
|
+
|
|
109
|
+
sz_move_t copy;
|
|
110
|
+
sz_move_t move;
|
|
111
|
+
sz_fill_t fill;
|
|
112
|
+
|
|
113
|
+
sz_find_byte_t find_byte;
|
|
114
|
+
sz_find_byte_t rfind_byte;
|
|
115
|
+
sz_find_t find;
|
|
116
|
+
sz_find_t rfind;
|
|
117
|
+
sz_find_set_t find_from_set;
|
|
118
|
+
sz_find_set_t rfind_from_set;
|
|
119
|
+
|
|
120
|
+
sz_edit_distance_t edit_distance;
|
|
121
|
+
sz_alignment_score_t alignment_score;
|
|
122
|
+
sz_hashes_t hashes;
|
|
123
|
+
|
|
124
|
+
} sz_implementations_t;
|
|
125
|
+
static sz_implementations_t sz_dispatch_table;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @brief Initializes a global static "virtual table" of supported backends
|
|
129
|
+
* Run it just once to avoiding unnecessary `if`-s.
|
|
130
|
+
*/
|
|
131
|
+
static void sz_dispatch_table_init(void) {
|
|
132
|
+
sz_implementations_t *impl = &sz_dispatch_table;
|
|
133
|
+
sz_capability_t caps = sz_capabilities();
|
|
134
|
+
sz_unused(caps); //< Unused when compiling on pre-SIMD machines.
|
|
135
|
+
|
|
136
|
+
impl->equal = sz_equal_serial;
|
|
137
|
+
impl->order = sz_order_serial;
|
|
138
|
+
impl->copy = sz_copy_serial;
|
|
139
|
+
impl->move = sz_move_serial;
|
|
140
|
+
impl->fill = sz_fill_serial;
|
|
141
|
+
|
|
142
|
+
impl->find = sz_find_serial;
|
|
143
|
+
impl->rfind = sz_rfind_serial;
|
|
144
|
+
impl->find_byte = sz_find_byte_serial;
|
|
145
|
+
impl->rfind_byte = sz_rfind_byte_serial;
|
|
146
|
+
impl->find_from_set = sz_find_charset_serial;
|
|
147
|
+
impl->rfind_from_set = sz_rfind_charset_serial;
|
|
148
|
+
|
|
149
|
+
impl->edit_distance = sz_edit_distance_serial;
|
|
150
|
+
impl->alignment_score = sz_alignment_score_serial;
|
|
151
|
+
impl->hashes = sz_hashes_serial;
|
|
152
|
+
|
|
153
|
+
#if SZ_USE_X86_AVX2
|
|
154
|
+
if (caps & sz_cap_x86_avx2_k) {
|
|
155
|
+
impl->copy = sz_copy_avx2;
|
|
156
|
+
impl->move = sz_move_avx2;
|
|
157
|
+
impl->fill = sz_fill_avx2;
|
|
158
|
+
impl->find_byte = sz_find_byte_avx2;
|
|
159
|
+
impl->rfind_byte = sz_rfind_byte_avx2;
|
|
160
|
+
impl->find = sz_find_avx2;
|
|
161
|
+
impl->rfind = sz_rfind_avx2;
|
|
162
|
+
}
|
|
163
|
+
#endif
|
|
164
|
+
|
|
165
|
+
#if SZ_USE_X86_AVX512
|
|
166
|
+
if (caps & sz_cap_x86_avx512f_k) {
|
|
167
|
+
impl->equal = sz_equal_avx512;
|
|
168
|
+
impl->order = sz_order_avx512;
|
|
169
|
+
impl->copy = sz_copy_avx512;
|
|
170
|
+
impl->move = sz_move_avx512;
|
|
171
|
+
impl->fill = sz_fill_avx512;
|
|
172
|
+
|
|
173
|
+
impl->find = sz_find_avx512;
|
|
174
|
+
impl->rfind = sz_rfind_avx512;
|
|
175
|
+
impl->find_byte = sz_find_byte_avx512;
|
|
176
|
+
impl->rfind_byte = sz_rfind_byte_avx512;
|
|
177
|
+
|
|
178
|
+
impl->edit_distance = sz_edit_distance_avx512;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if ((caps & sz_cap_x86_avx512f_k) && (caps & sz_cap_x86_avx512vl_k) && (caps & sz_cap_x86_gfni_k) &&
|
|
182
|
+
(caps & sz_cap_x86_avx512bw_k) && (caps & sz_cap_x86_avx512vbmi_k)) {
|
|
183
|
+
impl->find_from_set = sz_find_charset_avx512;
|
|
184
|
+
impl->rfind_from_set = sz_rfind_charset_avx512;
|
|
185
|
+
impl->alignment_score = sz_alignment_score_avx512;
|
|
186
|
+
}
|
|
187
|
+
#endif
|
|
188
|
+
|
|
189
|
+
#if SZ_USE_ARM_NEON
|
|
190
|
+
if (caps & sz_cap_arm_neon_k) {
|
|
191
|
+
impl->find = sz_find_neon;
|
|
192
|
+
impl->rfind = sz_rfind_neon;
|
|
193
|
+
impl->find_byte = sz_find_byte_neon;
|
|
194
|
+
impl->rfind_byte = sz_rfind_byte_neon;
|
|
195
|
+
impl->find_from_set = sz_find_charset_neon;
|
|
196
|
+
impl->rfind_from_set = sz_rfind_charset_neon;
|
|
197
|
+
}
|
|
198
|
+
#endif
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#if defined(_MSC_VER)
|
|
202
|
+
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
|
|
203
|
+
switch (fdwReason) {
|
|
204
|
+
case DLL_PROCESS_ATTACH: sz_dispatch_table_init(); return TRUE;
|
|
205
|
+
case DLL_THREAD_ATTACH: return TRUE;
|
|
206
|
+
case DLL_THREAD_DETACH: return TRUE;
|
|
207
|
+
case DLL_PROCESS_DETACH: return TRUE;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
#else
|
|
211
|
+
__attribute__((constructor)) static void sz_dispatch_table_init_on_gcc_or_clang(void) { sz_dispatch_table_init(); }
|
|
212
|
+
#endif
|
|
213
|
+
|
|
214
|
+
SZ_DYNAMIC sz_bool_t sz_equal(sz_cptr_t a, sz_cptr_t b, sz_size_t length) {
|
|
215
|
+
return sz_dispatch_table.equal(a, b, length);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
SZ_DYNAMIC sz_ordering_t sz_order(sz_cptr_t a, sz_size_t a_length, sz_cptr_t b, sz_size_t b_length) {
|
|
219
|
+
return sz_dispatch_table.order(a, a_length, b, b_length);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
SZ_DYNAMIC void sz_copy(sz_ptr_t target, sz_cptr_t source, sz_size_t length) {
|
|
223
|
+
sz_dispatch_table.copy(target, source, length);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
SZ_DYNAMIC void sz_move(sz_ptr_t target, sz_cptr_t source, sz_size_t length) {
|
|
227
|
+
sz_dispatch_table.move(target, source, length);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
SZ_DYNAMIC void sz_fill(sz_ptr_t target, sz_size_t length, sz_u8_t value) {
|
|
231
|
+
sz_dispatch_table.fill(target, length, value);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
SZ_DYNAMIC sz_cptr_t sz_find_byte(sz_cptr_t haystack, sz_size_t h_length, sz_cptr_t needle) {
|
|
235
|
+
return sz_dispatch_table.find_byte(haystack, h_length, needle);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
SZ_DYNAMIC sz_cptr_t sz_rfind_byte(sz_cptr_t haystack, sz_size_t h_length, sz_cptr_t needle) {
|
|
239
|
+
return sz_dispatch_table.rfind_byte(haystack, h_length, needle);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
SZ_DYNAMIC sz_cptr_t sz_find(sz_cptr_t haystack, sz_size_t h_length, sz_cptr_t needle, sz_size_t n_length) {
|
|
243
|
+
return sz_dispatch_table.find(haystack, h_length, needle, n_length);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
SZ_DYNAMIC sz_cptr_t sz_rfind(sz_cptr_t haystack, sz_size_t h_length, sz_cptr_t needle, sz_size_t n_length) {
|
|
247
|
+
return sz_dispatch_table.rfind(haystack, h_length, needle, n_length);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
SZ_DYNAMIC sz_cptr_t sz_find_charset(sz_cptr_t text, sz_size_t length, sz_charset_t const *set) {
|
|
251
|
+
return sz_dispatch_table.find_from_set(text, length, set);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
SZ_DYNAMIC sz_cptr_t sz_rfind_charset(sz_cptr_t text, sz_size_t length, sz_charset_t const *set) {
|
|
255
|
+
return sz_dispatch_table.rfind_from_set(text, length, set);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
SZ_DYNAMIC sz_size_t sz_edit_distance( //
|
|
259
|
+
sz_cptr_t a, sz_size_t a_length, //
|
|
260
|
+
sz_cptr_t b, sz_size_t b_length, //
|
|
261
|
+
sz_size_t bound, sz_memory_allocator_t *alloc) {
|
|
262
|
+
return sz_dispatch_table.edit_distance(a, a_length, b, b_length, bound, alloc);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
SZ_DYNAMIC sz_ssize_t sz_alignment_score(sz_cptr_t a, sz_size_t a_length, sz_cptr_t b, sz_size_t b_length,
|
|
266
|
+
sz_error_cost_t const *subs, sz_error_cost_t gap,
|
|
267
|
+
sz_memory_allocator_t *alloc) {
|
|
268
|
+
return sz_dispatch_table.alignment_score(a, a_length, b, b_length, subs, gap, alloc);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
SZ_DYNAMIC void sz_hashes(sz_cptr_t text, sz_size_t length, sz_size_t window_length, sz_size_t step, //
|
|
272
|
+
sz_hash_callback_t callback, void *callback_handle) {
|
|
273
|
+
sz_dispatch_table.hashes(text, length, window_length, step, callback, callback_handle);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
SZ_DYNAMIC sz_cptr_t sz_find_char_from(sz_cptr_t h, sz_size_t h_length, sz_cptr_t n, sz_size_t n_length) {
|
|
277
|
+
sz_charset_t set;
|
|
278
|
+
sz_charset_init(&set);
|
|
279
|
+
for (; n_length; ++n, --n_length) sz_charset_add(&set, *n);
|
|
280
|
+
return sz_find_charset(h, h_length, &set);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
SZ_DYNAMIC sz_cptr_t sz_find_char_not_from(sz_cptr_t h, sz_size_t h_length, sz_cptr_t n, sz_size_t n_length) {
|
|
284
|
+
sz_charset_t set;
|
|
285
|
+
sz_charset_init(&set);
|
|
286
|
+
for (; n_length; ++n, --n_length) sz_charset_add(&set, *n);
|
|
287
|
+
sz_charset_invert(&set);
|
|
288
|
+
return sz_find_charset(h, h_length, &set);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
SZ_DYNAMIC sz_cptr_t sz_rfind_char_from(sz_cptr_t h, sz_size_t h_length, sz_cptr_t n, sz_size_t n_length) {
|
|
292
|
+
sz_charset_t set;
|
|
293
|
+
sz_charset_init(&set);
|
|
294
|
+
for (; n_length; ++n, --n_length) sz_charset_add(&set, *n);
|
|
295
|
+
return sz_rfind_charset(h, h_length, &set);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
SZ_DYNAMIC sz_cptr_t sz_rfind_char_not_from(sz_cptr_t h, sz_size_t h_length, sz_cptr_t n, sz_size_t n_length) {
|
|
299
|
+
sz_charset_t set;
|
|
300
|
+
sz_charset_init(&set);
|
|
301
|
+
for (; n_length; ++n, --n_length) sz_charset_add(&set, *n);
|
|
302
|
+
sz_charset_invert(&set);
|
|
303
|
+
return sz_rfind_charset(h, h_length, &set);
|
|
304
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SIMD-accelerate CLI utilities based on StringZilla
|
|
2
|
+
|
|
3
|
+
## `wc`: Word Count
|
|
4
|
+
|
|
5
|
+
The `wc` utility on Linux can be used to count the number of lines, words, and bytes in a file.
|
|
6
|
+
Using SIMD-accelerated character and character-set search, StringZilla, even with slow SSDs, it can be noticeably faster.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
$ time wc enwik9.txt
|
|
10
|
+
13147025 129348346 1000000000 enwik9.txt
|
|
11
|
+
|
|
12
|
+
real 0m3.562s
|
|
13
|
+
user 0m3.470s
|
|
14
|
+
sys 0m0.092s
|
|
15
|
+
|
|
16
|
+
$ time cli/wc.py enwik9.txt
|
|
17
|
+
13147025 139132610 1000000000 enwik9.txt
|
|
18
|
+
|
|
19
|
+
real 0m1.165s
|
|
20
|
+
user 0m1.121s
|
|
21
|
+
sys 0m0.044s
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## `split`: Split File into Smaller Ones
|
|
25
|
+
|
|
26
|
+
The `split` utility on Linux can be used to split a file into smaller ones.
|
|
27
|
+
The current prototype only splits by line counts.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
$ time split -l 100000 enwik9.txt ...
|
|
31
|
+
|
|
32
|
+
real 0m6.424s
|
|
33
|
+
user 0m0.179s
|
|
34
|
+
sys 0m0.663s
|
|
35
|
+
|
|
36
|
+
$ time cli/split.py 100000 enwik9.txt ...
|
|
37
|
+
|
|
38
|
+
real 0m1.482s
|
|
39
|
+
user 0m1.020s
|
|
40
|
+
sys 0m0.460s
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
What other interfaces should be added?
|
|
46
|
+
|
|
47
|
+
- Levenshtein distances?
|
|
48
|
+
- Fuzzy search?
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from stringzilla import File, Str
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def split_file(file_path, lines_per_file, output_prefix):
|
|
9
|
+
try:
|
|
10
|
+
# 1. Memory-map the large file
|
|
11
|
+
file_mapped = File(file_path)
|
|
12
|
+
file_contents = Str(file_mapped)
|
|
13
|
+
|
|
14
|
+
# Variables to keep track of the current position and file part number
|
|
15
|
+
current_position = 0
|
|
16
|
+
file_part = 0
|
|
17
|
+
newline_position = (
|
|
18
|
+
-1
|
|
19
|
+
) # Start before file begins to find the first newline correctly
|
|
20
|
+
|
|
21
|
+
# Loop until the end of the file
|
|
22
|
+
while current_position < len(file_contents):
|
|
23
|
+
# 2. Loop to skip `lines_per_file` lines
|
|
24
|
+
for _ in range(lines_per_file):
|
|
25
|
+
newline_position = file_contents.find("\n", newline_position + 1)
|
|
26
|
+
if newline_position == -1: # No more newlines
|
|
27
|
+
break
|
|
28
|
+
|
|
29
|
+
# If no newlines were found and we're not at the start, process the rest of the file
|
|
30
|
+
if newline_position == -1 and current_position < len(file_contents):
|
|
31
|
+
newline_position = len(file_contents)
|
|
32
|
+
|
|
33
|
+
# 3. Use offset_within to get the length of the current section
|
|
34
|
+
# Assuming offset_within gives you the length from the current position
|
|
35
|
+
section_length = (
|
|
36
|
+
newline_position - current_position if newline_position != -1 else 0
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Extract the current section to write out
|
|
40
|
+
if section_length > 0: # Prevent creating empty files
|
|
41
|
+
current_slice = file_contents[current_position : newline_position + 1]
|
|
42
|
+
|
|
43
|
+
# 4. Save the current slice to file
|
|
44
|
+
output_path = f"{output_prefix}{file_part}"
|
|
45
|
+
current_slice.write_to(output_path)
|
|
46
|
+
|
|
47
|
+
# Prepare for the next slice
|
|
48
|
+
file_part += 1
|
|
49
|
+
current_position = newline_position + 1
|
|
50
|
+
|
|
51
|
+
except FileNotFoundError:
|
|
52
|
+
print(f"No such file: {file_path}")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f"An error occurred: {e}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main():
|
|
58
|
+
if len(sys.argv) < 4:
|
|
59
|
+
print(
|
|
60
|
+
"Usage: python split_file.py <lines_per_file> <input_file> <output_prefix>"
|
|
61
|
+
)
|
|
62
|
+
sys.exit(1)
|
|
63
|
+
|
|
64
|
+
lines_per_file = int(sys.argv[1])
|
|
65
|
+
file_path = sys.argv[2]
|
|
66
|
+
output_prefix = sys.argv[3]
|
|
67
|
+
|
|
68
|
+
split_file(file_path, lines_per_file, output_prefix)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from stringzilla import File, Str
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def wc(file_path):
|
|
9
|
+
try:
|
|
10
|
+
mapped_file = File(file_path)
|
|
11
|
+
mapped_bytes = Str(mapped_file)
|
|
12
|
+
line_count = mapped_bytes.count("\n")
|
|
13
|
+
word_count = mapped_bytes.count(" ")
|
|
14
|
+
char_count = mapped_bytes.__len__()
|
|
15
|
+
|
|
16
|
+
return line_count, word_count, char_count
|
|
17
|
+
except FileNotFoundError:
|
|
18
|
+
return f"No such file: {file_path}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
if len(sys.argv) < 2:
|
|
23
|
+
print("Usage: python wc.py <file>")
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
file_path = sys.argv[1]
|
|
27
|
+
counts = wc(file_path)
|
|
28
|
+
|
|
29
|
+
if isinstance(counts, tuple):
|
|
30
|
+
line_count, word_count, char_count = counts
|
|
31
|
+
print(f"{line_count} {word_count} {char_count} {file_path}")
|
|
32
|
+
else:
|
|
33
|
+
print(counts)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
main()
|