fuzzgpu 0.1.1__tar.gz → 0.1.3__tar.gz

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 (38) hide show
  1. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/Cargo.lock +2 -2
  2. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/PKG-INFO +6 -3
  3. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/README.md +2 -2
  4. fuzzgpu-0.1.3/assets/banner.svg +66 -0
  5. fuzzgpu-0.1.3/assets/github-stats.svg +51 -0
  6. fuzzgpu-0.1.3/assets/top-languages.svg +62 -0
  7. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/Cargo.toml +1 -1
  8. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/damerau.rs +3 -1
  9. fuzzgpu-0.1.3/crates/fuzzgpu-core/src/gpu.rs +146 -0
  10. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/jaro.rs +91 -46
  11. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/levenshtein.rs +58 -31
  12. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/needleman.rs +44 -39
  13. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/simd.rs +13 -13
  14. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-python/Cargo.toml +2 -2
  15. fuzzgpu-0.1.3/crates/fuzzgpu-python/src/lib.rs +465 -0
  16. fuzzgpu-0.1.3/crates/fuzzgpu-wasm/Cargo.toml +19 -0
  17. fuzzgpu-0.1.3/crates/fuzzgpu-wasm/src/lib.rs +92 -0
  18. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/pyproject.toml +5 -2
  19. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/python/fuzzgpu/__init__.py +21 -11
  20. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/python/fuzzgpu/fuzz.py +13 -2
  21. fuzzgpu-0.1.3/tests/test_api_signatures.py +171 -0
  22. fuzzgpu-0.1.3/tests/test_basic.py +323 -0
  23. fuzzgpu-0.1.3/tests/test_concurrency.py +62 -0
  24. fuzzgpu-0.1.3/tests/test_edge_cases.py +353 -0
  25. fuzzgpu-0.1.3/tests/test_invariants.py +169 -0
  26. fuzzgpu-0.1.3/tests/test_stress.py +123 -0
  27. fuzzgpu-0.1.1/crates/fuzzgpu-core/src/gpu.rs +0 -96
  28. fuzzgpu-0.1.1/crates/fuzzgpu-python/src/lib.rs +0 -276
  29. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/Cargo.toml +0 -0
  30. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/LICENSE +0 -0
  31. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/assets/logo.png +0 -0
  32. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/assets/logo.svg +0 -0
  33. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/fuzz.rs +0 -0
  34. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/lib.rs +0 -0
  35. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/shaders/jaro.wgsl +0 -0
  36. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/shaders/jaro_matrix.wgsl +0 -0
  37. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/shaders/levenshtein.wgsl +0 -0
  38. {fuzzgpu-0.1.1 → fuzzgpu-0.1.3}/crates/fuzzgpu-core/src/shaders/levenshtein_matrix.wgsl +0 -0
@@ -248,7 +248,7 @@ dependencies = [
248
248
 
249
249
  [[package]]
250
250
  name = "fuzzgpu-core"
251
- version = "0.1.1"
251
+ version = "0.1.3"
252
252
  dependencies = [
253
253
  "bytemuck",
254
254
  "log",
@@ -260,7 +260,7 @@ dependencies = [
260
260
 
261
261
  [[package]]
262
262
  name = "fuzzgpu-python"
263
- version = "0.1.1"
263
+ version = "0.1.3"
264
264
  dependencies = [
265
265
  "fuzzgpu-core",
266
266
  "pollster",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fuzzgpu
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -13,6 +13,9 @@ Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Rust
14
14
  Classifier: Topic :: Scientific/Engineering
15
15
  Classifier: Topic :: Text Processing :: General
16
+ Requires-Dist: pytest>=7.0 ; extra == 'test'
17
+ Requires-Dist: pytest-benchmark ; extra == 'test'
18
+ Provides-Extra: test
16
19
  License-File: LICENSE
17
20
  Summary: Hardware-accelerated fuzzy string matching & sequence alignment — cross-platform, no CUDA required
18
21
  Keywords: gpu,fuzzy,string-matching,levenshtein,damerau,jaro-winkler,needleman-wunsch,webgpu
@@ -35,7 +38,7 @@ Project-URL: Repository, https://github.com/kuntal-devrat/fuzzgpu
35
38
 
36
39
  *Cross-platform GPU compute via WebGPU (`wgpu`) & Multi-Core CPU parallelism with Rayon. Zero CUDA dependencies.*
37
40
 
38
- [![PyPI Version](https://img.shields.io/badge/pypi-v0.1.1-blue.svg?style=flat-square)](https://pypi.org/project/fuzzgpu/)
41
+ [![PyPI Version](https://img.shields.io/badge/pypi-v0.1.3-blue.svg?style=flat-square)](https://pypi.org/project/fuzzgpu/)
39
42
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](https://opensource.org/licenses/MIT)
40
43
  [![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg?style=flat-square)](https://www.rust-lang.org)
41
44
  [![Cross Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux%20%7C%20WASM-lightgrey.svg?style=flat-square)](https://github.com/kuntal-devrat/fuzzgpu)
@@ -100,7 +103,7 @@ pip install fuzzgpu
100
103
  ### Rust (Cargo.toml)
101
104
  ```toml
102
105
  [dependencies]
103
- fuzzgpu-core = "0.1.1"
106
+ fuzzgpu-core = "0.1.3"
104
107
  ```
105
108
 
106
109
  ---
@@ -8,7 +8,7 @@
8
8
 
9
9
  *Cross-platform GPU compute via WebGPU (`wgpu`) & Multi-Core CPU parallelism with Rayon. Zero CUDA dependencies.*
10
10
 
11
- [![PyPI Version](https://img.shields.io/badge/pypi-v0.1.1-blue.svg?style=flat-square)](https://pypi.org/project/fuzzgpu/)
11
+ [![PyPI Version](https://img.shields.io/badge/pypi-v0.1.3-blue.svg?style=flat-square)](https://pypi.org/project/fuzzgpu/)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](https://opensource.org/licenses/MIT)
13
13
  [![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg?style=flat-square)](https://www.rust-lang.org)
14
14
  [![Cross Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux%20%7C%20WASM-lightgrey.svg?style=flat-square)](https://github.com/kuntal-devrat/fuzzgpu)
@@ -73,7 +73,7 @@ pip install fuzzgpu
73
73
  ### Rust (Cargo.toml)
74
74
  ```toml
75
75
  [dependencies]
76
- fuzzgpu-core = "0.1.1"
76
+ fuzzgpu-core = "0.1.3"
77
77
  ```
78
78
 
79
79
  ---
@@ -0,0 +1,66 @@
1
+ <svg width="850" height="200" viewBox="0 0 850 200" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg_grad" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0B0F19" />
5
+ <stop offset="50%" stop-color="#111827" />
6
+ <stop offset="100%" stop-color="#0D1117" />
7
+ </linearGradient>
8
+ <linearGradient id="accent_grad" x1="0%" y1="0%" x2="100%" y2="0%">
9
+ <stop offset="0%" stop-color="#38BDF8" />
10
+ <stop offset="50%" stop-color="#818CF8" />
11
+ <stop offset="100%" stop-color="#C084FC" />
12
+ </linearGradient>
13
+ <linearGradient id="wave1" x1="0%" y1="0%" x2="100%" y2="0%">
14
+ <stop offset="0%" stop-color="#007ec6" stop-opacity="0.3" />
15
+ <stop offset="50%" stop-color="#8B5CF6" stop-opacity="0.4" />
16
+ <stop offset="100%" stop-color="#EC4899" stop-opacity="0.2" />
17
+ </linearGradient>
18
+ <linearGradient id="wave2" x1="0%" y1="0%" x2="100%" y2="0%">
19
+ <stop offset="0%" stop-color="#38BDF8" stop-opacity="0.15" />
20
+ <stop offset="100%" stop-color="#818CF8" stop-opacity="0.2" />
21
+ </linearGradient>
22
+ <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
23
+ <feGaussianBlur stdDeviation="6" result="blur" />
24
+ <feComposite in="SourceGraphic" in2="blur" operator="over" />
25
+ </filter>
26
+ </defs>
27
+
28
+ <!-- Background Card -->
29
+ <rect width="850" height="200" rx="16" fill="url(#bg_grad)" stroke="#30363D" stroke-width="1.5" />
30
+
31
+ <!-- Ambient Wave Layer -->
32
+ <path d="M0 130 C 200 90, 350 170, 550 120 C 700 80, 780 140, 850 110 L 850 200 L 0 200 Z" fill="url(#wave1)" />
33
+ <path d="M0 150 C 180 120, 380 180, 580 140 C 720 110, 800 160, 850 140 L 850 200 L 0 200 Z" fill="url(#wave2)" />
34
+
35
+ <!-- Neon Accent Line Top -->
36
+ <rect x="0" y="0" width="850" height="3" rx="1.5" fill="url(#accent_grad)" />
37
+
38
+ <!-- Title Text -->
39
+ <text x="425" y="78" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif" font-size="38" font-weight="800" fill="#F0F6FC" letter-spacing="1">
40
+ Devrat Kuntal
41
+ </text>
42
+
43
+ <!-- Subtitle / Tagline -->
44
+ <text x="425" y="112" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif" font-size="15" font-weight="600" fill="#38BDF8" letter-spacing="0.5">
45
+ ⚡ AI ARCHITECT | HIGH-PERFORMANCE SYSTEMS &amp; GPU COMPUTE ENGINEER
46
+ </text>
47
+
48
+ <!-- Tech tags pill badges -->
49
+ <g transform="translate(195, 140)">
50
+ <!-- Pill 1: WebGPU / WGSL -->
51
+ <rect x="0" y="0" width="105" height="24" rx="12" fill="#1F2937" stroke="#38BDF8" stroke-width="1" />
52
+ <text x="52.5" y="16" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="11" font-weight="600" fill="#E0F2FE">⚡ WebGPU/WGSL</text>
53
+
54
+ <!-- Pill 2: Rust SIMD -->
55
+ <rect x="115" y="0" width="95" height="24" rx="12" fill="#1F2937" stroke="#DEA584" stroke-width="1" />
56
+ <text x="162.5" y="16" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="11" font-weight="600" fill="#FFEDD5">🦀 Rust SIMD</text>
57
+
58
+ <!-- Pill 3: PyO3 ABI3 -->
59
+ <rect x="220" y="0" width="95" height="24" rx="12" fill="#1F2937" stroke="#818CF8" stroke-width="1" />
60
+ <text x="267.5" y="16" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="11" font-weight="600" fill="#E0E7FF">🐍 PyO3 ABI3</text>
61
+
62
+ <!-- Pill 4: AI Agent Runtimes -->
63
+ <rect x="325" y="0" width="135" height="24" rx="12" fill="#1F2937" stroke="#EC4899" stroke-width="1" />
64
+ <text x="392.5" y="16" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="11" font-weight="600" fill="#FCE7F3">🤖 AI Agent Runtimes</text>
65
+ </g>
66
+ </svg>
@@ -0,0 +1,51 @@
1
+ <svg width="415" height="195" viewBox="0 0 415 195" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="card_bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0D1117" />
5
+ <stop offset="100%" stop-color="#161B22" />
6
+ </linearGradient>
7
+ <linearGradient id="icon_grad" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" stop-color="#38BDF8" />
9
+ <stop offset="100%" stop-color="#8B5CF6" />
10
+ </linearGradient>
11
+ </defs>
12
+
13
+ <rect width="415" height="195" rx="12" fill="url(#card_bg)" stroke="#30363D" stroke-width="1" />
14
+
15
+ <!-- Header -->
16
+ <text x="25" y="35" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif" font-size="18" font-weight="700" fill="#38BDF8">
17
+ GitHub Analytics
18
+ </text>
19
+
20
+ <!-- Metric 1: Total Stars -->
21
+ <g transform="translate(25, 60)">
22
+ <circle cx="10" cy="10" r="10" fill="#1F2937" />
23
+ <text x="10" y="14" text-anchor="middle" font-size="11">⭐</text>
24
+ <text x="30" y="15" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Total Stars Earned:</text>
25
+ <text x="365" y="15" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="14" font-weight="700" fill="#F0F6FC">4</text>
26
+ </g>
27
+
28
+ <!-- Metric 2: Total Commits (2025/2026) -->
29
+ <g transform="translate(25, 90)">
30
+ <circle cx="10" cy="10" r="10" fill="#1F2937" />
31
+ <text x="10" y="14" text-anchor="middle" font-size="11">⚡</text>
32
+ <text x="30" y="15" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Total Commits (Recent):</text>
33
+ <text x="365" y="15" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="14" font-weight="700" fill="#F0F6FC">335+</text>
34
+ </g>
35
+
36
+ <!-- Metric 3: Total PRs -->
37
+ <g transform="translate(25, 120)">
38
+ <circle cx="10" cy="10" r="10" fill="#1F2937" />
39
+ <text x="10" y="14" text-anchor="middle" font-size="11">🔀</text>
40
+ <text x="30" y="15" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Pull Requests:</text>
41
+ <text x="365" y="15" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="14" font-weight="700" fill="#F0F6FC">12</text>
42
+ </g>
43
+
44
+ <!-- Metric 4: Total Repositories -->
45
+ <g transform="translate(25, 150)">
46
+ <circle cx="10" cy="10" r="10" fill="#1F2937" />
47
+ <text x="10" y="14" text-anchor="middle" font-size="11">📦</text>
48
+ <text x="30" y="15" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Active Repositories:</text>
49
+ <text x="365" y="15" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="14" font-weight="700" fill="#F0F6FC">26</text>
50
+ </g>
51
+ </svg>
@@ -0,0 +1,62 @@
1
+ <svg width="415" height="195" viewBox="0 0 415 195" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="lang_bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0D1117" />
5
+ <stop offset="100%" stop-color="#161B22" />
6
+ </linearGradient>
7
+ <clipPath id="bar_clip">
8
+ <rect x="25" y="55" width="365" height="9" rx="4.5" />
9
+ </clipPath>
10
+ </defs>
11
+
12
+ <rect width="415" height="195" rx="12" fill="url(#lang_bg)" stroke="#30363D" stroke-width="1" />
13
+
14
+ <!-- Header -->
15
+ <text x="25" y="35" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif" font-size="18" font-weight="700" fill="#38BDF8">
16
+ Most Used Languages
17
+ </text>
18
+
19
+ <!-- Stacked Bar -->
20
+ <g clip-path="url(#bar_clip)">
21
+ <rect x="25.0" y="55" width="169.3" height="9" fill="#3178c6" />
22
+ <rect x="194.3" y="55" width="75.4" height="9" fill="#f1e05a" />
23
+ <rect x="269.7" y="55" width="37.0" height="9" fill="#3572A5" />
24
+ <rect x="306.7" y="55" width="28.1" height="9" fill="#dea584" />
25
+ <rect x="334.8" y="55" width="25.4" height="9" fill="#663399" />
26
+ <rect x="360.2" y="55" width="7.9" height="9" fill="#A97BFF" />
27
+
28
+ </g>
29
+
30
+ <!-- Language List -->
31
+
32
+ <g transform="translate(25, 90)">
33
+ <circle cx="6" cy="6" r="5" fill="#3178c6" />
34
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">TypeScript</text>
35
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">46.4%</text>
36
+ </g>
37
+ <g transform="translate(25, 118)">
38
+ <circle cx="6" cy="6" r="5" fill="#f1e05a" />
39
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">JavaScript</text>
40
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">20.7%</text>
41
+ </g>
42
+ <g transform="translate(25, 146)">
43
+ <circle cx="6" cy="6" r="5" fill="#3572A5" />
44
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Python</text>
45
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">10.1%</text>
46
+ </g>
47
+ <g transform="translate(215, 90)">
48
+ <circle cx="6" cy="6" r="5" fill="#dea584" />
49
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Rust</text>
50
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">7.7%</text>
51
+ </g>
52
+ <g transform="translate(215, 118)">
53
+ <circle cx="6" cy="6" r="5" fill="#663399" />
54
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">CSS</text>
55
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">7.0%</text>
56
+ </g>
57
+ <g transform="translate(215, 146)">
58
+ <circle cx="6" cy="6" r="5" fill="#A97BFF" />
59
+ <text x="20" y="10" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="500" fill="#94A3B8">Kotlin</text>
60
+ <text x="160" y="10" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, sans-serif" font-size="13" font-weight="600" fill="#F0F6FC">2.2%</text>
61
+ </g>
62
+ </svg>
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fuzzgpu-core"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  edition = "2021"
5
5
  description = "GPU-accelerated fuzzy string matching engine"
6
6
  license = "MIT"
@@ -16,6 +16,7 @@ pub fn damerau_levenshtein_distance(a: &str, b: &str) -> u32 {
16
16
  }
17
17
 
18
18
  fn damerau_bytes(a: &[u8], b: &[u8]) -> u32 {
19
+ debug_assert!(a.is_ascii() && b.is_ascii(), "damerau_bytes requires ASCII inputs");
19
20
  let (m, n) = (a.len(), b.len());
20
21
  if m == 0 { return n as u32; }
21
22
  if n == 0 { return m as u32; }
@@ -152,7 +153,8 @@ pub fn damerau_levenshtein_cdist(list_a: &[&str], list_b: &[&str]) -> Vec<Vec<u3
152
153
  }).collect()
153
154
  }
154
155
 
155
- /// Damerau-Levenshtein normalized ratio (0.0 to 100.0) based on Sørensen-Dice formula.
156
+ /// Damerau-Levenshtein normalized ratio (0.0 to 100.0) based on standard edit distance similarity formula:
157
+ /// `((total_len - dist) / total_len) * 100.0`.
156
158
  pub fn damerau_ratio(s1: &str, s2: &str) -> f64 {
157
159
  let len_a = if s1.is_ascii() { s1.len() } else { s1.chars().count() };
158
160
  let len_b = if s2.is_ascii() { s2.len() } else { s2.chars().count() };
@@ -0,0 +1,146 @@
1
+ use std::sync::atomic::{AtomicBool, Ordering};
2
+ use std::sync::{Arc, OnceLock};
3
+ use thiserror::Error;
4
+
5
+ #[derive(Error, Debug)]
6
+ pub enum FuzzGpuError {
7
+ #[error("GPU not available: {0}")]
8
+ NoDevice(String),
9
+ #[error("Shader compilation failed: {0}")]
10
+ ShaderError(String),
11
+ #[error("Buffer error: {0}")]
12
+ BufferError(String),
13
+ #[error("GPU operation timed out: {0}")]
14
+ Timeout(String),
15
+ #[error("Invalid input or parameters: {0}")]
16
+ InvalidInput(String),
17
+ }
18
+
19
+ pub type Result<T> = std::result::Result<T, FuzzGpuError>;
20
+
21
+ pub struct GpuEngine {
22
+ pub device: wgpu::Device,
23
+ pub queue: wgpu::Queue,
24
+ pub info: GpuInfo,
25
+ pub max_buffer_size: u64,
26
+ pub max_storage_buffer_binding_size: u32,
27
+ }
28
+
29
+ static GLOBAL_ENGINE: OnceLock<Arc<GpuEngine>> = OnceLock::new();
30
+ static CPU_ONLY_FLAG: AtomicBool = AtomicBool::new(false);
31
+ static ENV_CPU_CHECK: OnceLock<bool> = OnceLock::new();
32
+
33
+ #[derive(Debug, Clone)]
34
+ pub struct GpuInfo {
35
+ pub name: String,
36
+ pub backend: String,
37
+ }
38
+
39
+ impl GpuEngine {
40
+ /// Enable or disable CPU-only fallback globally.
41
+ pub fn set_cpu_only(cpu_only: bool) {
42
+ CPU_ONLY_FLAG.store(cpu_only, Ordering::SeqCst);
43
+ }
44
+
45
+ /// Check whether CPU-only mode is active (cached, zero syscall overhead in loops).
46
+ pub fn is_cpu_only() -> bool {
47
+ if CPU_ONLY_FLAG.load(Ordering::Relaxed) {
48
+ return true;
49
+ }
50
+ *ENV_CPU_CHECK.get_or_init(|| {
51
+ if let Ok(v) = std::env::var("FUZZGPU_USE_CPU") {
52
+ v == "1" || v.eq_ignore_ascii_case("true") || v.eq_ignore_ascii_case("yes")
53
+ } else {
54
+ false
55
+ }
56
+ })
57
+ }
58
+
59
+ /// Returns whether a GPU device is available and ready for compute.
60
+ pub fn is_available() -> bool {
61
+ if Self::is_cpu_only() {
62
+ return false;
63
+ }
64
+ Self::get().is_ok()
65
+ }
66
+
67
+ /// Get or lazily initialize the singleton GPU engine.
68
+ pub fn get() -> Result<Arc<Self>> {
69
+ if Self::is_cpu_only() {
70
+ return Err(FuzzGpuError::NoDevice("CPU-only mode is active (FUZZGPU_USE_CPU)".into()));
71
+ }
72
+
73
+ if let Some(engine) = GLOBAL_ENGINE.get() {
74
+ return Ok(Arc::clone(engine));
75
+ }
76
+
77
+ let engine = pollster::block_on(Self::new_inner())?;
78
+ let _ = GLOBAL_ENGINE.set(Arc::clone(&engine));
79
+ Ok(engine)
80
+ }
81
+
82
+ async fn new_inner() -> Result<Arc<Self>> {
83
+ let instance = wgpu::Instance::default();
84
+
85
+ let adapter = match instance
86
+ .request_adapter(&wgpu::RequestAdapterOptions {
87
+ power_preference: wgpu::PowerPreference::HighPerformance,
88
+ compatible_surface: None,
89
+ force_fallback_adapter: false,
90
+ })
91
+ .await
92
+ {
93
+ Some(a) => a,
94
+ None => instance
95
+ .request_adapter(&wgpu::RequestAdapterOptions {
96
+ power_preference: wgpu::PowerPreference::None,
97
+ compatible_surface: None,
98
+ force_fallback_adapter: true,
99
+ })
100
+ .await
101
+ .ok_or_else(|| FuzzGpuError::NoDevice("No GPU adapter found".into()))?,
102
+ };
103
+
104
+ let adapter_info = adapter.get_info();
105
+ let info = GpuInfo {
106
+ name: adapter_info.name.clone(),
107
+ backend: format!("{:?}", adapter_info.backend),
108
+ };
109
+
110
+ // Query adapter limits dynamically rather than hardcoding static limits
111
+ let adapter_limits = adapter.limits();
112
+ let target_storage_size = (128 * 1024 * 1024).min(adapter_limits.max_storage_buffer_binding_size);
113
+ let target_buffer_size = (128 * 1024 * 1024).min(adapter_limits.max_buffer_size);
114
+
115
+ let required_limits = wgpu::Limits {
116
+ max_storage_buffer_binding_size: target_storage_size,
117
+ max_buffer_size: target_buffer_size,
118
+ max_compute_workgroup_storage_size: 16384.min(adapter_limits.max_compute_workgroup_storage_size),
119
+ max_compute_invocations_per_workgroup: 256.min(adapter_limits.max_compute_invocations_per_workgroup),
120
+ ..Default::default()
121
+ };
122
+
123
+ let (device, queue) = adapter
124
+ .request_device(
125
+ &wgpu::DeviceDescriptor {
126
+ label: Some("fuzzgpu device"),
127
+ required_features: wgpu::Features::empty(),
128
+ required_limits,
129
+ memory_hints: Default::default(),
130
+ },
131
+ None,
132
+ )
133
+ .await
134
+ .map_err(|e| FuzzGpuError::NoDevice(format!("Failed to create GPU device: {}", e)))?;
135
+
136
+ let engine = Arc::new(Self {
137
+ device,
138
+ queue,
139
+ info,
140
+ max_buffer_size: target_buffer_size as u64,
141
+ max_storage_buffer_binding_size: target_storage_size,
142
+ });
143
+
144
+ Ok(engine)
145
+ }
146
+ }