rufio 0.33.0 → 0.34.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -8
- data/lib/rufio/async_scanner_fiber.rb +154 -0
- data/lib/rufio/async_scanner_promise.rb +66 -0
- data/lib/rufio/command_logger.rb +3 -0
- data/lib/rufio/native/rufio_zig.bundle +0 -0
- data/lib/rufio/native_scanner.rb +252 -233
- data/lib/rufio/native_scanner_zig.rb +215 -82
- data/lib/rufio/parallel_scanner.rb +173 -0
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +3 -1
- data/lib_zig/rufio_native/Makefile +2 -1
- data/lib_zig/rufio_native/src/main.zig +328 -117
- data/lib_zig/rufio_native/src/main.zig.sync +205 -0
- metadata +7 -10
- data/lib/rufio/native/rufio_native.bundle +0 -0
- data/lib/rufio/native_scanner_magnus.rb +0 -194
- data/lib_rust/rufio_native/.cargo/config.toml +0 -2
- data/lib_rust/rufio_native/Cargo.lock +0 -346
- data/lib_rust/rufio_native/Cargo.toml +0 -18
- data/lib_rust/rufio_native/build.rs +0 -46
- data/lib_rust/rufio_native/src/lib.rs +0 -197
- /data/docs/{CHANGELOG_v0.33.0.md → CHANGELOG_v0.34.0.md} +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fiddle'
|
|
4
|
+
require 'fiddle/import'
|
|
5
|
+
|
|
3
6
|
module Rufio
|
|
4
|
-
# Zig
|
|
5
|
-
#
|
|
6
|
-
module
|
|
7
|
+
# Zig拡張の非同期FFI層
|
|
8
|
+
# Ruby側は「ハンドル(u64)だけ」を持つ
|
|
9
|
+
module NativeScannerZigFFI
|
|
10
|
+
extend Fiddle::Importer
|
|
11
|
+
|
|
7
12
|
LIB_PATH = File.expand_path('native/rufio_zig.bundle', __dir__)
|
|
8
13
|
|
|
9
14
|
@loaded = false
|
|
@@ -17,12 +22,24 @@ module Rufio
|
|
|
17
22
|
|
|
18
23
|
if File.exist?(LIB_PATH)
|
|
19
24
|
begin
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
# 動的ライブラリをロード
|
|
26
|
+
dlload LIB_PATH
|
|
27
|
+
|
|
28
|
+
# ABI Boundary: Ruby ABI非依存のC関数(非同期版)
|
|
29
|
+
extern 'uint64_t core_async_create()'
|
|
30
|
+
extern 'int32_t core_async_scan(uint64_t, const char*)'
|
|
31
|
+
extern 'int32_t core_async_scan_fast(uint64_t, const char*, size_t)'
|
|
32
|
+
extern 'uint8_t core_async_get_state(uint64_t)'
|
|
33
|
+
extern 'void core_async_get_progress(uint64_t, void*, void*)'
|
|
34
|
+
extern 'void core_async_cancel(uint64_t)'
|
|
35
|
+
extern 'size_t core_async_get_count(uint64_t)'
|
|
36
|
+
extern 'size_t core_async_get_name(uint64_t, size_t, void*, size_t)'
|
|
37
|
+
extern 'int32_t core_async_get_attrs(uint64_t, size_t, void*, void*, void*, void*, void*)'
|
|
38
|
+
extern 'void core_async_destroy(uint64_t)'
|
|
39
|
+
extern 'char* core_async_version()'
|
|
40
|
+
|
|
41
|
+
@available = true
|
|
42
|
+
rescue StandardError => e
|
|
26
43
|
warn "Failed to load zig extension: #{e.message}" if ENV['RUFIO_DEBUG']
|
|
27
44
|
@available = false
|
|
28
45
|
end
|
|
@@ -37,12 +54,150 @@ module Rufio
|
|
|
37
54
|
load! unless @loaded
|
|
38
55
|
@available
|
|
39
56
|
end
|
|
57
|
+
|
|
58
|
+
# バージョン取得
|
|
59
|
+
def version
|
|
60
|
+
core_async_version.to_s
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# 非同期スキャナークラス(ポーリングベース)
|
|
66
|
+
class NativeScannerZigCore
|
|
67
|
+
POLL_INTERVAL = 0.01 # 10ms
|
|
68
|
+
|
|
69
|
+
def initialize
|
|
70
|
+
@handle = NativeScannerZigFFI.core_async_create
|
|
71
|
+
raise StandardError, "Failed to create scanner" if @handle.zero?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# 非同期スキャン開始
|
|
75
|
+
def scan_async(path)
|
|
76
|
+
result = NativeScannerZigFFI.core_async_scan(@handle, path)
|
|
77
|
+
raise StandardError, "Failed to start scan" if result != 0
|
|
78
|
+
self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# 高速スキャン(エントリ数制限付き)
|
|
82
|
+
def scan_fast_async(path, max_entries)
|
|
83
|
+
result = NativeScannerZigFFI.core_async_scan_fast(@handle, path, max_entries)
|
|
84
|
+
raise StandardError, "Failed to start scan" if result != 0
|
|
85
|
+
self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# ポーリングして完了待ち
|
|
89
|
+
def wait(timeout: nil)
|
|
90
|
+
start_time = Time.now
|
|
91
|
+
loop do
|
|
92
|
+
state = get_state
|
|
93
|
+
|
|
94
|
+
case state
|
|
95
|
+
when :done
|
|
96
|
+
return get_results
|
|
97
|
+
when :failed
|
|
98
|
+
raise StandardError, "Scan failed"
|
|
99
|
+
when :cancelled
|
|
100
|
+
raise StandardError, "Scan cancelled"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if timeout && (Time.now - start_time) > timeout
|
|
104
|
+
raise StandardError, "Timeout"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
sleep POLL_INTERVAL
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# 進捗報告付きで完了待ち
|
|
112
|
+
def wait_with_progress(&block)
|
|
113
|
+
loop do
|
|
114
|
+
state = get_state
|
|
115
|
+
progress = get_progress
|
|
116
|
+
|
|
117
|
+
yield(progress[:current], progress[:total]) if block_given?
|
|
118
|
+
|
|
119
|
+
case state
|
|
120
|
+
when :done
|
|
121
|
+
return get_results
|
|
122
|
+
when :failed
|
|
123
|
+
raise StandardError, "Scan failed"
|
|
124
|
+
when :cancelled
|
|
125
|
+
raise StandardError, "Scan cancelled"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
sleep POLL_INTERVAL
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# 状態確認
|
|
133
|
+
def get_state
|
|
134
|
+
state_code = NativeScannerZigFFI.core_async_get_state(@handle)
|
|
135
|
+
[:idle, :scanning, :done, :cancelled, :failed][state_code] || :failed
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# 進捗取得
|
|
139
|
+
def get_progress
|
|
140
|
+
current = Fiddle::Pointer.malloc(8)
|
|
141
|
+
total = Fiddle::Pointer.malloc(8)
|
|
142
|
+
NativeScannerZigFFI.core_async_get_progress(@handle, current, total)
|
|
143
|
+
{
|
|
144
|
+
current: current[0, 8].unpack1('Q'),
|
|
145
|
+
total: total[0, 8].unpack1('Q')
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# キャンセル
|
|
150
|
+
def cancel
|
|
151
|
+
NativeScannerZigFFI.core_async_cancel(@handle)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# 結果取得(完了後)
|
|
155
|
+
def get_results
|
|
156
|
+
count = NativeScannerZigFFI.core_async_get_count(@handle)
|
|
157
|
+
entries = []
|
|
158
|
+
count.times { |i| entries << get_entry(i) }
|
|
159
|
+
entries
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# スキャナーを明示的に破棄
|
|
163
|
+
def close
|
|
164
|
+
return if @handle.zero?
|
|
165
|
+
|
|
166
|
+
NativeScannerZigFFI.core_async_destroy(@handle)
|
|
167
|
+
@handle = 0
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
# 指定インデックスのエントリを取得
|
|
173
|
+
def get_entry(index)
|
|
174
|
+
# 名前を取得
|
|
175
|
+
name_buf = Fiddle::Pointer.malloc(256)
|
|
176
|
+
name_len = NativeScannerZigFFI.core_async_get_name(@handle, index, name_buf, 256)
|
|
177
|
+
name = name_buf[0, name_len].force_encoding('UTF-8')
|
|
178
|
+
|
|
179
|
+
# 属性を取得
|
|
180
|
+
is_dir = Fiddle::Pointer.malloc(1)
|
|
181
|
+
size = Fiddle::Pointer.malloc(8)
|
|
182
|
+
mtime = Fiddle::Pointer.malloc(8)
|
|
183
|
+
executable = Fiddle::Pointer.malloc(1)
|
|
184
|
+
hidden = Fiddle::Pointer.malloc(1)
|
|
185
|
+
|
|
186
|
+
NativeScannerZigFFI.core_async_get_attrs(@handle, index, is_dir, size, mtime, executable, hidden)
|
|
187
|
+
|
|
188
|
+
{
|
|
189
|
+
name: name,
|
|
190
|
+
is_dir: is_dir[0, 1].unpack1('C') != 0,
|
|
191
|
+
size: size[0, 8].unpack1('Q'),
|
|
192
|
+
mtime: mtime[0, 8].unpack1('q'),
|
|
193
|
+
executable: executable[0, 1].unpack1('C') != 0,
|
|
194
|
+
hidden: hidden[0, 1].unpack1('C') != 0
|
|
195
|
+
}
|
|
40
196
|
end
|
|
41
197
|
end
|
|
42
198
|
|
|
43
|
-
# Zig
|
|
44
|
-
if
|
|
45
|
-
# NativeScannerにzigモードを追加
|
|
199
|
+
# Zig拡張が利用可能な場合のみ、NativeScannerに統合
|
|
200
|
+
if NativeScannerZigFFI.load!
|
|
46
201
|
class NativeScanner
|
|
47
202
|
class << self
|
|
48
203
|
# zigモードを追加
|
|
@@ -51,9 +206,9 @@ module Rufio
|
|
|
51
206
|
def mode=(value)
|
|
52
207
|
case value
|
|
53
208
|
when 'zig'
|
|
54
|
-
if
|
|
209
|
+
if NativeScannerZigFFI.available?
|
|
55
210
|
@mode = 'zig'
|
|
56
|
-
@current_library = nil
|
|
211
|
+
@current_library = nil
|
|
57
212
|
else
|
|
58
213
|
@mode = 'ruby'
|
|
59
214
|
@current_library = nil
|
|
@@ -63,44 +218,57 @@ module Rufio
|
|
|
63
218
|
end
|
|
64
219
|
end
|
|
65
220
|
|
|
66
|
-
# zig
|
|
221
|
+
# zigスキャン(ポーリング方式)
|
|
67
222
|
def scan_directory_with_zig(path)
|
|
68
223
|
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
69
224
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
225
|
+
# 非同期スキャナーを作成してスキャン
|
|
226
|
+
scanner = NativeScannerZigCore.new
|
|
227
|
+
begin
|
|
228
|
+
scanner.scan_async(path)
|
|
229
|
+
entries = scanner.wait(timeout: 60)
|
|
230
|
+
|
|
231
|
+
# 結果の形式を統一(type フィールドを追加)
|
|
232
|
+
entries.map do |entry|
|
|
233
|
+
{
|
|
234
|
+
name: entry[:name],
|
|
235
|
+
type: entry[:is_dir] ? 'directory' : 'file',
|
|
236
|
+
size: entry[:size],
|
|
237
|
+
mtime: entry[:mtime],
|
|
238
|
+
mode: 0,
|
|
239
|
+
executable: entry[:executable],
|
|
240
|
+
hidden: entry[:hidden]
|
|
241
|
+
}
|
|
242
|
+
end
|
|
243
|
+
ensure
|
|
244
|
+
scanner.close
|
|
83
245
|
end
|
|
84
246
|
rescue StandardError => e
|
|
85
247
|
raise StandardError, "Zig scan failed: #{e.message}"
|
|
86
248
|
end
|
|
87
249
|
|
|
88
|
-
# zig
|
|
250
|
+
# zigで高速スキャン(ポーリング方式)
|
|
89
251
|
def scan_directory_fast_with_zig(path, max_entries)
|
|
90
252
|
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
91
253
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
254
|
+
scanner = NativeScannerZigCore.new
|
|
255
|
+
begin
|
|
256
|
+
scanner.scan_fast_async(path, max_entries)
|
|
257
|
+
entries = scanner.wait(timeout: 60)
|
|
258
|
+
|
|
259
|
+
entries.map do |entry|
|
|
260
|
+
{
|
|
261
|
+
name: entry[:name],
|
|
262
|
+
type: entry[:is_dir] ? 'directory' : 'file',
|
|
263
|
+
size: entry[:size],
|
|
264
|
+
mtime: entry[:mtime],
|
|
265
|
+
mode: 0,
|
|
266
|
+
executable: entry[:executable],
|
|
267
|
+
hidden: entry[:hidden]
|
|
268
|
+
}
|
|
269
|
+
end
|
|
270
|
+
ensure
|
|
271
|
+
scanner.close
|
|
104
272
|
end
|
|
105
273
|
rescue StandardError => e
|
|
106
274
|
raise StandardError, "Zig fast scan failed: #{e.message}"
|
|
@@ -115,13 +283,6 @@ module Rufio
|
|
|
115
283
|
case @mode
|
|
116
284
|
when 'zig'
|
|
117
285
|
scan_directory_with_zig(path)
|
|
118
|
-
when 'magnus'
|
|
119
|
-
# magnusモードの場合は、native_scanner_magnus.rbで定義されたメソッドを呼ぶ
|
|
120
|
-
if defined?(NativeScannerMagnusLoader) && respond_to?(:scan_directory_with_magnus, true)
|
|
121
|
-
scan_directory_with_magnus(path)
|
|
122
|
-
else
|
|
123
|
-
original_scan_directory(path)
|
|
124
|
-
end
|
|
125
286
|
else
|
|
126
287
|
original_scan_directory(path)
|
|
127
288
|
end
|
|
@@ -136,13 +297,6 @@ module Rufio
|
|
|
136
297
|
case @mode
|
|
137
298
|
when 'zig'
|
|
138
299
|
scan_directory_fast_with_zig(path, max_entries)
|
|
139
|
-
when 'magnus'
|
|
140
|
-
# magnusモードの場合は、native_scanner_magnus.rbで定義されたメソッドを呼ぶ
|
|
141
|
-
if defined?(NativeScannerMagnusLoader) && respond_to?(:scan_directory_fast_with_magnus, true)
|
|
142
|
-
scan_directory_fast_with_magnus(path, max_entries)
|
|
143
|
-
else
|
|
144
|
-
original_scan_directory_fast(path, max_entries)
|
|
145
|
-
end
|
|
146
300
|
else
|
|
147
301
|
original_scan_directory_fast(path, max_entries)
|
|
148
302
|
end
|
|
@@ -156,7 +310,7 @@ module Rufio
|
|
|
156
310
|
|
|
157
311
|
case @mode
|
|
158
312
|
when 'zig'
|
|
159
|
-
|
|
313
|
+
NativeScannerZigFFI.version
|
|
160
314
|
else
|
|
161
315
|
original_version
|
|
162
316
|
end
|
|
@@ -167,50 +321,29 @@ module Rufio
|
|
|
167
321
|
|
|
168
322
|
def available_libraries
|
|
169
323
|
original = original_available_libraries
|
|
170
|
-
|
|
171
|
-
# magnusが既に追加されていなければ追加
|
|
172
|
-
if defined?(NativeScannerMagnusLoader) && !result.key?(:magnus)
|
|
173
|
-
result = result.merge(magnus: NativeScannerMagnusLoader.available?)
|
|
174
|
-
end
|
|
175
|
-
result
|
|
324
|
+
original.merge(zig: NativeScannerZigFFI.available?)
|
|
176
325
|
end
|
|
177
326
|
|
|
178
|
-
# autoモードの優先順位を更新(
|
|
327
|
+
# autoモードの優先順位を更新(zig > ruby)
|
|
179
328
|
def mode=(value)
|
|
180
329
|
case value
|
|
181
330
|
when 'auto'
|
|
182
|
-
# 優先順位:
|
|
183
|
-
if
|
|
184
|
-
@mode = 'magnus'
|
|
185
|
-
@current_library = nil
|
|
186
|
-
elsif NativeScannerZigLoader.available?
|
|
331
|
+
# 優先順位: Zig > Ruby
|
|
332
|
+
if NativeScannerZigFFI.available?
|
|
187
333
|
@mode = 'zig'
|
|
188
334
|
@current_library = nil
|
|
189
|
-
elsif defined?(RustLib) && RustLib.available?
|
|
190
|
-
@mode = 'rust'
|
|
191
|
-
@current_library = RustLib
|
|
192
|
-
elsif defined?(GoLib) && GoLib.available?
|
|
193
|
-
@mode = 'go'
|
|
194
|
-
@current_library = GoLib
|
|
195
335
|
else
|
|
196
336
|
@mode = 'ruby'
|
|
197
337
|
@current_library = nil
|
|
198
338
|
end
|
|
199
339
|
when 'zig'
|
|
200
|
-
if
|
|
340
|
+
if NativeScannerZigFFI.available?
|
|
201
341
|
@mode = 'zig'
|
|
202
342
|
@current_library = nil
|
|
203
343
|
else
|
|
204
344
|
@mode = 'ruby'
|
|
205
345
|
@current_library = nil
|
|
206
346
|
end
|
|
207
|
-
when 'magnus'
|
|
208
|
-
if defined?(NativeScannerMagnusLoader) && NativeScannerMagnusLoader.available?
|
|
209
|
-
@mode = 'magnus'
|
|
210
|
-
@current_library = nil
|
|
211
|
-
else
|
|
212
|
-
send(:original_mode=, value)
|
|
213
|
-
end
|
|
214
347
|
else
|
|
215
348
|
send(:original_mode=, value)
|
|
216
349
|
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thread'
|
|
4
|
+
|
|
5
|
+
module Rufio
|
|
6
|
+
# 並列スキャン最適化クラス
|
|
7
|
+
#
|
|
8
|
+
# 複数のディレクトリを並列にスキャンし、結果をマージします。
|
|
9
|
+
# スレッドプールを使用して効率的に並列処理を行います。
|
|
10
|
+
#
|
|
11
|
+
# 使用例:
|
|
12
|
+
# parallel_scanner = ParallelScanner.new(max_workers: 4)
|
|
13
|
+
# results = parallel_scanner.scan_all(['/path1', '/path2', '/path3'])
|
|
14
|
+
# all_entries = parallel_scanner.scan_all_merged(['/path1', '/path2'])
|
|
15
|
+
#
|
|
16
|
+
class ParallelScanner
|
|
17
|
+
DEFAULT_MAX_WORKERS = 4
|
|
18
|
+
|
|
19
|
+
# @param max_workers [Integer] 最大ワーカー数
|
|
20
|
+
# @param backend [Symbol] バックエンド (:ruby or :zig)
|
|
21
|
+
def initialize(max_workers: DEFAULT_MAX_WORKERS, backend: :ruby)
|
|
22
|
+
@max_workers = max_workers
|
|
23
|
+
@backend = backend
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# 複数のディレクトリを並列スキャン
|
|
27
|
+
#
|
|
28
|
+
# @param paths [Array<String>] スキャンするディレクトリパスのリスト
|
|
29
|
+
# @return [Array<Hash>] 各ディレクトリのスキャン結果
|
|
30
|
+
# [{path:, entries:, success:, error:}, ...]
|
|
31
|
+
def scan_all(paths)
|
|
32
|
+
return [] if paths.empty?
|
|
33
|
+
|
|
34
|
+
results = []
|
|
35
|
+
mutex = Mutex.new
|
|
36
|
+
queue = Queue.new
|
|
37
|
+
|
|
38
|
+
# キューにパスを追加
|
|
39
|
+
paths.each { |path| queue << path }
|
|
40
|
+
|
|
41
|
+
# ワーカースレッドを作成
|
|
42
|
+
workers = []
|
|
43
|
+
worker_count = [@max_workers, paths.length].min
|
|
44
|
+
|
|
45
|
+
worker_count.times do
|
|
46
|
+
workers << Thread.new do
|
|
47
|
+
loop do
|
|
48
|
+
path = queue.pop(true) rescue nil
|
|
49
|
+
break if path.nil?
|
|
50
|
+
|
|
51
|
+
result = scan_single_directory(path)
|
|
52
|
+
mutex.synchronize { results << result }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 全ワーカーの完了を待つ
|
|
58
|
+
workers.each(&:join)
|
|
59
|
+
|
|
60
|
+
results
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# 複数のディレクトリを並列スキャンし、結果をマージ
|
|
64
|
+
#
|
|
65
|
+
# @param paths [Array<String>] スキャンするディレクトリパスのリスト
|
|
66
|
+
# @yield [entry] 各エントリをフィルタリングするブロック(オプション)
|
|
67
|
+
# @return [Array<Hash>] 全エントリのマージされた配列
|
|
68
|
+
def scan_all_merged(paths, &filter)
|
|
69
|
+
results = scan_all(paths)
|
|
70
|
+
|
|
71
|
+
# 成功した結果のみを取得
|
|
72
|
+
all_entries = results
|
|
73
|
+
.select { |r| r[:success] }
|
|
74
|
+
.flat_map { |r| r[:entries] }
|
|
75
|
+
|
|
76
|
+
# フィルタが指定されている場合は適用
|
|
77
|
+
all_entries = all_entries.select(&filter) if block_given?
|
|
78
|
+
|
|
79
|
+
all_entries
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# 進捗報告付き並列スキャン
|
|
83
|
+
#
|
|
84
|
+
# @param paths [Array<String>] スキャンするディレクトリパスのリスト
|
|
85
|
+
# @yield [completed, total] 進捗情報を受け取るブロック
|
|
86
|
+
# @return [Array<Hash>] 各ディレクトリのスキャン結果
|
|
87
|
+
def scan_all_with_progress(paths, &block)
|
|
88
|
+
return [] if paths.empty?
|
|
89
|
+
|
|
90
|
+
results = []
|
|
91
|
+
mutex = Mutex.new
|
|
92
|
+
queue = Queue.new
|
|
93
|
+
completed = 0
|
|
94
|
+
total = paths.length
|
|
95
|
+
|
|
96
|
+
# キューにパスを追加
|
|
97
|
+
paths.each { |path| queue << path }
|
|
98
|
+
|
|
99
|
+
# ワーカースレッドを作成
|
|
100
|
+
workers = []
|
|
101
|
+
worker_count = [@max_workers, paths.length].min
|
|
102
|
+
|
|
103
|
+
worker_count.times do
|
|
104
|
+
workers << Thread.new do
|
|
105
|
+
loop do
|
|
106
|
+
path = queue.pop(true) rescue nil
|
|
107
|
+
break if path.nil?
|
|
108
|
+
|
|
109
|
+
result = scan_single_directory(path)
|
|
110
|
+
|
|
111
|
+
mutex.synchronize do
|
|
112
|
+
results << result
|
|
113
|
+
completed += 1
|
|
114
|
+
yield(completed, total) if block_given?
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# 全ワーカーの完了を待つ
|
|
121
|
+
workers.each(&:join)
|
|
122
|
+
|
|
123
|
+
results
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# 単一のディレクトリをスキャン
|
|
129
|
+
#
|
|
130
|
+
# @param path [String] スキャンするディレクトリパス
|
|
131
|
+
# @return [Hash] スキャン結果 {path:, entries:, success:, error:}
|
|
132
|
+
def scan_single_directory(path)
|
|
133
|
+
scanner = create_scanner
|
|
134
|
+
|
|
135
|
+
begin
|
|
136
|
+
scanner.scan_async(path)
|
|
137
|
+
entries = scanner.wait(timeout: 60)
|
|
138
|
+
|
|
139
|
+
{
|
|
140
|
+
path: path,
|
|
141
|
+
entries: entries,
|
|
142
|
+
success: true
|
|
143
|
+
}
|
|
144
|
+
rescue StandardError => e
|
|
145
|
+
{
|
|
146
|
+
path: path,
|
|
147
|
+
entries: [],
|
|
148
|
+
success: false,
|
|
149
|
+
error: e.message
|
|
150
|
+
}
|
|
151
|
+
ensure
|
|
152
|
+
scanner.close
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# バックエンドに応じたスキャナーを作成
|
|
157
|
+
#
|
|
158
|
+
# @return [NativeScannerRubyCore, NativeScannerZigCore] スキャナーインスタンス
|
|
159
|
+
def create_scanner
|
|
160
|
+
case @backend
|
|
161
|
+
when :zig
|
|
162
|
+
if defined?(NativeScannerZigFFI) && NativeScannerZigFFI.available?
|
|
163
|
+
NativeScannerZigCore.new
|
|
164
|
+
else
|
|
165
|
+
# Zigが利用できない場合はRubyにフォールバック
|
|
166
|
+
NativeScannerRubyCore.new
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
NativeScannerRubyCore.new
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
data/lib/rufio/version.rb
CHANGED
data/lib/rufio.rb
CHANGED
|
@@ -33,8 +33,10 @@ require_relative "rufio/shell_command_completion"
|
|
|
33
33
|
require_relative "rufio/command_logger"
|
|
34
34
|
require_relative "rufio/background_command_executor"
|
|
35
35
|
require_relative "rufio/native_scanner"
|
|
36
|
-
require_relative "rufio/native_scanner_magnus"
|
|
37
36
|
require_relative "rufio/native_scanner_zig"
|
|
37
|
+
require_relative "rufio/async_scanner_promise"
|
|
38
|
+
require_relative "rufio/async_scanner_fiber"
|
|
39
|
+
require_relative "rufio/parallel_scanner"
|
|
38
40
|
|
|
39
41
|
# プロジェクトモード
|
|
40
42
|
require_relative "rufio/project_mode"
|
|
@@ -7,13 +7,14 @@ ZIG := zig
|
|
|
7
7
|
RUBY_INCLUDE := $(shell $(RUBY) -e "puts RbConfig::CONFIG['rubyhdrdir']")
|
|
8
8
|
RUBY_ARCH_INCLUDE := $(shell $(RUBY) -e "puts RbConfig::CONFIG['rubyarchhdrdir']")
|
|
9
9
|
RUBY_LIB_DIR := $(shell $(RUBY) -e "puts RbConfig::CONFIG['libdir']")
|
|
10
|
+
RUBY_VERSION := $(shell $(RUBY) -e "puts RbConfig::CONFIG['MAJOR'] + '.' + RbConfig::CONFIG['MINOR']")
|
|
10
11
|
|
|
11
12
|
# コンパイルフラグ
|
|
12
13
|
ZIG_FLAGS := -O ReleaseFast -dynamic -lc
|
|
13
14
|
ZIG_FLAGS += -I$(RUBY_INCLUDE)
|
|
14
15
|
ZIG_FLAGS += -I$(RUBY_ARCH_INCLUDE)
|
|
15
16
|
ZIG_FLAGS += -L$(RUBY_LIB_DIR)
|
|
16
|
-
ZIG_FLAGS += -lruby
|
|
17
|
+
ZIG_FLAGS += -lruby.$(RUBY_VERSION)
|
|
17
18
|
|
|
18
19
|
# ターゲット
|
|
19
20
|
TARGET := librufio_zig.dylib
|