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.
Files changed (66) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
  2. package/.github/workflows/prerelease.yml +33 -7
  3. package/.github/workflows/release.yml +25 -26
  4. package/.gitmodules +4 -1
  5. package/CITATION.cff +1 -1
  6. package/CMakeLists.txt +6 -8
  7. package/CONTRIBUTING.md +46 -1
  8. package/Cargo.toml +1 -1
  9. package/README.md +7 -6
  10. package/VERSION +1 -1
  11. package/c/lib.cpp +19 -17
  12. package/c/test.c +41 -7
  13. package/c/usearch.h +15 -15
  14. package/conanfile.py +1 -1
  15. package/cpp/test.cpp +9 -2
  16. package/include/usearch/index.hpp +15 -15
  17. package/include/usearch/index_dense.hpp +8 -4
  18. package/include/usearch/index_plugins.hpp +34 -23
  19. package/javascript/usearch.js +1 -1
  20. package/package.json +3 -2
  21. package/pyproject.toml +2 -2
  22. package/setup.py +10 -4
  23. package/simsimd/.releaserc +3 -2
  24. package/simsimd/Cargo.lock +708 -0
  25. package/simsimd/Cargo.toml +36 -0
  26. package/simsimd/README.md +37 -1
  27. package/simsimd/build.rs +20 -0
  28. package/simsimd/include/simsimd/simsimd.h +4 -4
  29. package/simsimd/include/simsimd/spatial.h +1 -1
  30. package/simsimd/include/simsimd/types.h +5 -1
  31. package/simsimd/package.json +1 -1
  32. package/simsimd/rust/benches/cosine.rs +27 -0
  33. package/simsimd/rust/benches/native.rs +27 -0
  34. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  35. package/simsimd/rust/lib.c +68 -0
  36. package/simsimd/rust/lib.rs +232 -0
  37. package/sqlite/README.md +178 -0
  38. package/sqlite/lib.cpp +325 -0
  39. package/stringzilla/CONTRIBUTING.md +429 -0
  40. package/stringzilla/Cargo.lock +25 -0
  41. package/stringzilla/Cargo.toml +26 -0
  42. package/stringzilla/LICENSE +201 -0
  43. package/stringzilla/README.md +1281 -0
  44. package/stringzilla/VERSION +1 -0
  45. package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
  46. package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
  47. package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
  48. package/stringzilla/binding.gyp +16 -0
  49. package/stringzilla/build.rs +14 -0
  50. package/stringzilla/c/lib.c +304 -0
  51. package/stringzilla/cli/README.md +48 -0
  52. package/stringzilla/cli/split.py +72 -0
  53. package/stringzilla/cli/wc.py +37 -0
  54. package/stringzilla/include/stringzilla/experimental.h +724 -0
  55. package/stringzilla/include/stringzilla/module.modulemap +9 -0
  56. package/stringzilla/include/stringzilla/stringzilla.h +5240 -0
  57. package/stringzilla/include/stringzilla/stringzilla.hpp +3830 -0
  58. package/stringzilla/javascript/lib.c +125 -0
  59. package/stringzilla/javascript/stringzilla.js +22 -0
  60. package/stringzilla/package-ci.json +10 -0
  61. package/stringzilla/package.json +32 -0
  62. package/stringzilla/rust/lib.rs +362 -0
  63. package/stringzilla/swift/StringProtocol+StringZilla.swift +280 -0
  64. package/stringzilla/swift/Test.swift +60 -0
  65. package/wasmer.toml +1 -1
  66. package/simsimd/bench.js +0 -51
@@ -0,0 +1 @@
1
+ 3.2.0
@@ -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()