rufio 0.32.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.
@@ -0,0 +1,378 @@
1
+ const std = @import("std");
2
+ const c = @cImport({
3
+ @cInclude("dirent.h");
4
+ @cInclude("sys/stat.h");
5
+ @cInclude("string.h");
6
+ @cInclude("stdlib.h");
7
+ @cInclude("pthread.h");
8
+ });
9
+
10
+ /// スキャン状態
11
+ pub const ScanState = enum(u8) {
12
+ idle = 0,
13
+ scanning = 1,
14
+ done = 2,
15
+ cancelled = 3,
16
+ failed = 4,
17
+ };
18
+
19
+ /// ディレクトリエントリ情報(内部保持用)
20
+ const DirEntry = struct {
21
+ name: []u8,
22
+ is_dir: bool,
23
+ size: u64,
24
+ mtime: i64,
25
+ executable: bool,
26
+ hidden: bool,
27
+
28
+ fn deinit(self: *DirEntry) void {
29
+ c.free(self.name.ptr);
30
+ }
31
+ };
32
+
33
+ /// 非同期スキャナーコア(状態を保持)
34
+ const AsyncScanner = struct {
35
+ entries: []DirEntry,
36
+ capacity: usize,
37
+ count: usize,
38
+ state: ScanState,
39
+ progress: usize,
40
+ total_estimate: usize,
41
+ mutex: c.pthread_mutex_t,
42
+ path_copy: ?[*:0]u8,
43
+ max_entries: usize,
44
+ thread: c.pthread_t,
45
+ thread_started: bool,
46
+
47
+ fn init() !*AsyncScanner {
48
+ const self = @as(?*AsyncScanner, @ptrCast(@alignCast(c.malloc(@sizeOf(AsyncScanner))))) orelse return error.OutOfMemory;
49
+
50
+ self.*.capacity = 64;
51
+ const entries_size = self.*.capacity * @sizeOf(DirEntry);
52
+ const entries_ptr = c.malloc(entries_size) orelse {
53
+ c.free(self);
54
+ return error.OutOfMemory;
55
+ };
56
+ self.*.entries = @as([*]DirEntry, @ptrCast(@alignCast(entries_ptr)))[0..self.*.capacity];
57
+ self.*.count = 0;
58
+ self.*.state = .idle;
59
+ self.*.progress = 0;
60
+ self.*.total_estimate = 0;
61
+ self.*.path_copy = null;
62
+ self.*.max_entries = 0;
63
+ self.*.thread_started = false;
64
+
65
+ // mutex初期化
66
+ _ = c.pthread_mutex_init(&self.*.mutex, null);
67
+
68
+ return self;
69
+ }
70
+
71
+ fn deinit(self: *AsyncScanner) void {
72
+ // スレッドが実行中なら待機
73
+ if (self.thread_started) {
74
+ _ = c.pthread_join(self.thread, null);
75
+ }
76
+
77
+ // 各エントリの名前を解放
78
+ for (self.entries[0..self.count]) |*entry| {
79
+ entry.deinit();
80
+ }
81
+
82
+ // パスのコピーを解放
83
+ if (self.path_copy) |path| {
84
+ c.free(path);
85
+ }
86
+
87
+ // エントリ配列を解放
88
+ c.free(self.entries.ptr);
89
+
90
+ // mutex破棄
91
+ _ = c.pthread_mutex_destroy(&self.mutex);
92
+
93
+ // 自身を解放
94
+ c.free(self);
95
+ }
96
+
97
+ fn clear(self: *AsyncScanner) void {
98
+ // 既存のエントリをクリア
99
+ for (self.entries[0..self.count]) |*entry| {
100
+ entry.deinit();
101
+ }
102
+ self.count = 0;
103
+ self.progress = 0;
104
+ self.total_estimate = 0;
105
+ }
106
+
107
+ fn ensureCapacity(self: *AsyncScanner, needed: usize) !void {
108
+ if (needed <= self.capacity) return;
109
+
110
+ const new_capacity = @max(needed, self.capacity * 2);
111
+ const new_size = new_capacity * @sizeOf(DirEntry);
112
+ const new_ptr = c.realloc(self.entries.ptr, new_size) orelse return error.OutOfMemory;
113
+ self.entries = @as([*]DirEntry, @ptrCast(@alignCast(new_ptr)))[0..new_capacity];
114
+ self.capacity = new_capacity;
115
+ }
116
+
117
+ fn isCancelled(self: *AsyncScanner) bool {
118
+ _ = c.pthread_mutex_lock(&self.mutex);
119
+ defer _ = c.pthread_mutex_unlock(&self.mutex);
120
+ return self.state == .cancelled;
121
+ }
122
+
123
+ fn setState(self: *AsyncScanner, new_state: ScanState) void {
124
+ _ = c.pthread_mutex_lock(&self.mutex);
125
+ defer _ = c.pthread_mutex_unlock(&self.mutex);
126
+ self.state = new_state;
127
+ }
128
+
129
+ fn scan(self: *AsyncScanner, path: [*:0]const u8, max_entries: usize) !void {
130
+ // クリア
131
+ self.clear();
132
+
133
+ const dir = c.opendir(path) orelse return error.CannotOpenDir;
134
+ defer _ = c.closedir(dir);
135
+
136
+ const path_len = c.strlen(path);
137
+ const path_slice = path[0..path_len];
138
+
139
+ while (c.readdir(dir)) |entry| {
140
+ // キャンセルチェック
141
+ if (self.isCancelled()) {
142
+ return error.Cancelled;
143
+ }
144
+
145
+ // エントリ数制限チェック
146
+ if (max_entries > 0 and self.count >= max_entries) break;
147
+
148
+ const name_ptr = @as([*:0]const u8, @ptrCast(&entry.*.d_name));
149
+ const name_len = c.strlen(name_ptr);
150
+ const name_slice = name_ptr[0..name_len];
151
+
152
+ // "." と ".." をスキップ
153
+ if (std.mem.eql(u8, name_slice, ".") or std.mem.eql(u8, name_slice, "..")) {
154
+ continue;
155
+ }
156
+
157
+ // 容量確保
158
+ try self.ensureCapacity(self.count + 1);
159
+
160
+ // フルパスを構築
161
+ var path_buf: [std.fs.max_path_bytes]u8 = undefined;
162
+ const full_path = std.fmt.bufPrintZ(&path_buf, "{s}/{s}", .{ path_slice, name_slice }) catch continue;
163
+
164
+ // ファイル情報を取得
165
+ var stat_buf: c.struct_stat = undefined;
166
+ if (c.lstat(full_path.ptr, &stat_buf) != 0) {
167
+ continue;
168
+ }
169
+
170
+ // 名前をコピー
171
+ const name_copy_ptr = c.malloc(name_len + 1) orelse return error.OutOfMemory;
172
+ const name_copy_bytes = @as([*]u8, @ptrCast(@alignCast(name_copy_ptr)));
173
+ const name_copy = name_copy_bytes[0..name_len];
174
+ @memcpy(name_copy, name_slice);
175
+ name_copy_bytes[name_len] = 0;
176
+
177
+ // エントリを追加
178
+ self.entries[self.count] = DirEntry{
179
+ .name = name_copy,
180
+ .is_dir = (stat_buf.st_mode & c.S_IFMT) == c.S_IFDIR,
181
+ .size = @intCast(stat_buf.st_size),
182
+ .mtime = @intCast(stat_buf.st_mtimespec.tv_sec),
183
+ .executable = (stat_buf.st_mode & c.S_IXUSR) != 0,
184
+ .hidden = name_slice[0] == '.',
185
+ };
186
+ self.count += 1;
187
+
188
+ // 進捗更新
189
+ _ = c.pthread_mutex_lock(&self.mutex);
190
+ self.progress = self.count;
191
+ _ = c.pthread_mutex_unlock(&self.mutex);
192
+ }
193
+ }
194
+
195
+ fn scanAsync(self: *AsyncScanner, path: [*:0]const u8, max_entries: usize) !void {
196
+ _ = c.pthread_mutex_lock(&self.mutex);
197
+ defer _ = c.pthread_mutex_unlock(&self.mutex);
198
+
199
+ if (self.state == .scanning) {
200
+ return error.AlreadyScanning;
201
+ }
202
+
203
+ // パスをコピー
204
+ const path_len = c.strlen(path);
205
+ const path_copy_ptr = c.malloc(path_len + 1) orelse return error.OutOfMemory;
206
+ const path_copy_bytes = @as([*:0]u8, @ptrCast(@alignCast(path_copy_ptr)));
207
+ @memcpy(path_copy_bytes[0..path_len], path[0..path_len]);
208
+ path_copy_bytes[path_len] = 0;
209
+
210
+ // 古いパスを解放
211
+ if (self.path_copy) |old_path| {
212
+ c.free(old_path);
213
+ }
214
+
215
+ self.path_copy = path_copy_bytes;
216
+ self.max_entries = max_entries;
217
+ self.state = .scanning;
218
+
219
+ // スレッド開始
220
+ const result = c.pthread_create(&self.thread, null, scanWorkerEntry, self);
221
+ if (result != 0) {
222
+ self.state = .failed;
223
+ return error.ThreadCreateFailed;
224
+ }
225
+ self.thread_started = true;
226
+ }
227
+
228
+ fn scanWorker(self: *AsyncScanner) void {
229
+ const path = self.path_copy orelse {
230
+ self.setState(.failed);
231
+ return;
232
+ };
233
+
234
+ self.scan(path, self.max_entries) catch |err| {
235
+ // キャンセルエラーの場合は状態を変更しない(既にcancelledになっている)
236
+ if (err == error.Cancelled) {
237
+ return;
238
+ }
239
+ self.setState(.failed);
240
+ return;
241
+ };
242
+
243
+ self.setState(.done);
244
+ }
245
+ };
246
+
247
+ // スレッドエントリポイント
248
+ fn scanWorkerEntry(arg: ?*anyopaque) callconv(.c) ?*anyopaque {
249
+ const scanner = @as(*AsyncScanner, @ptrCast(@alignCast(arg)));
250
+ scanner.scanWorker();
251
+ return null;
252
+ }
253
+
254
+ // ============================================================
255
+ // ABI Boundary: Ruby ABI非依存・ハンドルベース
256
+ // ============================================================
257
+
258
+ /// 非同期スキャナーを作成(ハンドル返却)
259
+ export fn core_async_create() u64 {
260
+ const scanner = AsyncScanner.init() catch return 0;
261
+ return @intFromPtr(scanner);
262
+ }
263
+
264
+ /// 非同期スキャン開始
265
+ export fn core_async_scan(handle: u64, path: [*:0]const u8) i32 {
266
+ if (handle == 0) return -1;
267
+
268
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
269
+ scanner.scanAsync(path, 0) catch return -1;
270
+ return 0;
271
+ }
272
+
273
+ /// 高速スキャン(エントリ数制限付き)
274
+ export fn core_async_scan_fast(handle: u64, path: [*:0]const u8, max_entries: usize) i32 {
275
+ if (handle == 0) return -1;
276
+
277
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
278
+ scanner.scanAsync(path, max_entries) catch return -1;
279
+ return 0;
280
+ }
281
+
282
+ /// 状態取得
283
+ export fn core_async_get_state(handle: u64) u8 {
284
+ if (handle == 0) return @intFromEnum(ScanState.failed);
285
+
286
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
287
+ _ = c.pthread_mutex_lock(&scanner.mutex);
288
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
289
+ return @intFromEnum(scanner.state);
290
+ }
291
+
292
+ /// 進捗取得
293
+ export fn core_async_get_progress(handle: u64, current: *usize, total: *usize) void {
294
+ if (handle == 0) {
295
+ current.* = 0;
296
+ total.* = 0;
297
+ return;
298
+ }
299
+
300
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
301
+ _ = c.pthread_mutex_lock(&scanner.mutex);
302
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
303
+
304
+ current.* = scanner.progress;
305
+ total.* = scanner.total_estimate;
306
+ }
307
+
308
+ /// キャンセル
309
+ export fn core_async_cancel(handle: u64) void {
310
+ if (handle == 0) return;
311
+
312
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
313
+ _ = c.pthread_mutex_lock(&scanner.mutex);
314
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
315
+
316
+ if (scanner.state == .scanning) {
317
+ scanner.state = .cancelled;
318
+ }
319
+ }
320
+
321
+ /// エントリ数を取得
322
+ export fn core_async_get_count(handle: u64) usize {
323
+ if (handle == 0) return 0;
324
+
325
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
326
+ _ = c.pthread_mutex_lock(&scanner.mutex);
327
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
328
+ return scanner.count;
329
+ }
330
+
331
+ /// 指定インデックスのエントリ名を取得
332
+ export fn core_async_get_name(handle: u64, index: usize, buf: [*]u8, buf_size: usize) usize {
333
+ if (handle == 0) return 0;
334
+
335
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
336
+ _ = c.pthread_mutex_lock(&scanner.mutex);
337
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
338
+
339
+ if (index >= scanner.count) return 0;
340
+
341
+ const entry = &scanner.entries[index];
342
+ const copy_len = @min(entry.name.len, buf_size - 1);
343
+ @memcpy(buf[0..copy_len], entry.name[0..copy_len]);
344
+ buf[copy_len] = 0;
345
+ return copy_len;
346
+ }
347
+
348
+ /// 指定インデックスのエントリ属性を取得
349
+ export fn core_async_get_attrs(handle: u64, index: usize, is_dir: *u8, size: *u64, mtime: *i64, executable: *u8, hidden: *u8) i32 {
350
+ if (handle == 0) return -1;
351
+
352
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
353
+ _ = c.pthread_mutex_lock(&scanner.mutex);
354
+ defer _ = c.pthread_mutex_unlock(&scanner.mutex);
355
+
356
+ if (index >= scanner.count) return -1;
357
+
358
+ const entry = &scanner.entries[index];
359
+ is_dir.* = if (entry.is_dir) 1 else 0;
360
+ size.* = entry.size;
361
+ mtime.* = entry.mtime;
362
+ executable.* = if (entry.executable) 1 else 0;
363
+ hidden.* = if (entry.hidden) 1 else 0;
364
+ return 0;
365
+ }
366
+
367
+ /// スキャナーを破棄
368
+ export fn core_async_destroy(handle: u64) void {
369
+ if (handle == 0) return;
370
+
371
+ const scanner = @as(*AsyncScanner, @ptrFromInt(handle));
372
+ scanner.deinit();
373
+ }
374
+
375
+ /// バージョン情報
376
+ export fn core_async_version() [*:0]const u8 {
377
+ return "4.0.0-async";
378
+ }
@@ -0,0 +1,205 @@
1
+ const std = @import("std");
2
+ const c = @cImport({
3
+ @cInclude("dirent.h");
4
+ @cInclude("sys/stat.h");
5
+ @cInclude("string.h");
6
+ @cInclude("stdlib.h");
7
+ });
8
+
9
+ /// ディレクトリエントリ情報(内部保持用)
10
+ const DirEntry = struct {
11
+ name: []u8,
12
+ is_dir: bool,
13
+ size: u64,
14
+ mtime: i64,
15
+ executable: bool,
16
+ hidden: bool,
17
+
18
+ fn deinit(self: *DirEntry) void {
19
+ c.free(self.name.ptr);
20
+ }
21
+ };
22
+
23
+ /// スキャナーコア(状態を保持)
24
+ const Scanner = struct {
25
+ entries: []DirEntry,
26
+ capacity: usize,
27
+ count: usize,
28
+
29
+ fn init() !*Scanner {
30
+ const self = @as(?*Scanner, @ptrCast(@alignCast(c.malloc(@sizeOf(Scanner))))) orelse return error.OutOfMemory;
31
+ self.*.capacity = 64;
32
+ const entries_size = self.*.capacity * @sizeOf(DirEntry);
33
+ const entries_ptr = c.malloc(entries_size) orelse {
34
+ c.free(self);
35
+ return error.OutOfMemory;
36
+ };
37
+ self.*.entries = @as([*]DirEntry, @ptrCast(@alignCast(entries_ptr)))[0..self.*.capacity];
38
+ self.*.count = 0;
39
+ return self;
40
+ }
41
+
42
+ fn deinit(self: *Scanner) void {
43
+ // 各エントリの名前を解放
44
+ for (self.entries[0..self.count]) |*entry| {
45
+ entry.deinit();
46
+ }
47
+ // エントリ配列を解放
48
+ c.free(self.entries.ptr);
49
+ // 自身を解放
50
+ c.free(self);
51
+ }
52
+
53
+ fn clear(self: *Scanner) void {
54
+ // 既存のエントリをクリア
55
+ for (self.entries[0..self.count]) |*entry| {
56
+ entry.deinit();
57
+ }
58
+ self.count = 0;
59
+ }
60
+
61
+ fn ensureCapacity(self: *Scanner, needed: usize) !void {
62
+ if (needed <= self.capacity) return;
63
+
64
+ const new_capacity = @max(needed, self.capacity * 2);
65
+ const new_size = new_capacity * @sizeOf(DirEntry);
66
+ const new_ptr = c.realloc(self.entries.ptr, new_size) orelse return error.OutOfMemory;
67
+ self.entries = @as([*]DirEntry, @ptrCast(@alignCast(new_ptr)))[0..new_capacity];
68
+ self.capacity = new_capacity;
69
+ }
70
+
71
+ fn scan(self: *Scanner, path: [*:0]const u8, max_entries: usize) !void {
72
+ // 既存のエントリをクリア
73
+ self.clear();
74
+
75
+ const dir = c.opendir(path) orelse return error.CannotOpenDir;
76
+ defer _ = c.closedir(dir);
77
+
78
+ const path_len = c.strlen(path);
79
+ const path_slice = path[0..path_len];
80
+
81
+ while (c.readdir(dir)) |entry| {
82
+ // エントリ数制限チェック
83
+ if (max_entries > 0 and self.count >= max_entries) break;
84
+
85
+ const name_ptr = @as([*:0]const u8, @ptrCast(&entry.*.d_name));
86
+ const name_len = c.strlen(name_ptr);
87
+ const name_slice = name_ptr[0..name_len];
88
+
89
+ // "." と ".." をスキップ
90
+ if (std.mem.eql(u8, name_slice, ".") or std.mem.eql(u8, name_slice, "..")) {
91
+ continue;
92
+ }
93
+
94
+ // 容量確保
95
+ try self.ensureCapacity(self.count + 1);
96
+
97
+ // フルパスを構築
98
+ var path_buf: [std.fs.max_path_bytes]u8 = undefined;
99
+ const full_path = std.fmt.bufPrintZ(&path_buf, "{s}/{s}", .{ path_slice, name_slice }) catch continue;
100
+
101
+ // ファイル情報を取得
102
+ var stat_buf: c.struct_stat = undefined;
103
+ if (c.lstat(full_path.ptr, &stat_buf) != 0) {
104
+ continue;
105
+ }
106
+
107
+ // 名前をコピー
108
+ const name_copy_ptr = c.malloc(name_len + 1) orelse return error.OutOfMemory;
109
+ const name_copy_bytes = @as([*]u8, @ptrCast(@alignCast(name_copy_ptr)));
110
+ const name_copy = name_copy_bytes[0..name_len];
111
+ @memcpy(name_copy, name_slice);
112
+ name_copy_bytes[name_len] = 0;
113
+
114
+ // エントリを追加
115
+ self.entries[self.count] = DirEntry{
116
+ .name = name_copy,
117
+ .is_dir = (stat_buf.st_mode & c.S_IFMT) == c.S_IFDIR,
118
+ .size = @intCast(stat_buf.st_size),
119
+ .mtime = @intCast(stat_buf.st_mtimespec.tv_sec),
120
+ .executable = (stat_buf.st_mode & c.S_IXUSR) != 0,
121
+ .hidden = name_slice[0] == '.',
122
+ };
123
+ self.count += 1;
124
+ }
125
+ }
126
+ };
127
+
128
+ // ============================================================
129
+ // ABI Boundary: Ruby ABI非依存・ハンドルベース
130
+ // ============================================================
131
+
132
+ /// スキャナーを作成(ハンドル返却)
133
+ export fn core_create() u64 {
134
+ const scanner = Scanner.init() catch return 0;
135
+ return @intFromPtr(scanner);
136
+ }
137
+
138
+ /// ディレクトリをスキャン
139
+ export fn core_scan(handle: u64, path: [*:0]const u8) i32 {
140
+ if (handle == 0) return -1;
141
+
142
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
143
+ scanner.scan(path, 0) catch return -1;
144
+ return 0;
145
+ }
146
+
147
+ /// 高速スキャン(エントリ数制限付き)
148
+ export fn core_scan_fast(handle: u64, path: [*:0]const u8, max_entries: usize) i32 {
149
+ if (handle == 0) return -1;
150
+
151
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
152
+ scanner.scan(path, max_entries) catch return -1;
153
+ return 0;
154
+ }
155
+
156
+ /// エントリ数を取得
157
+ export fn core_get_count(handle: u64) usize {
158
+ if (handle == 0) return 0;
159
+
160
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
161
+ return scanner.count;
162
+ }
163
+
164
+ /// 指定インデックスのエントリ名を取得
165
+ export fn core_get_name(handle: u64, index: usize, buf: [*]u8, buf_size: usize) usize {
166
+ if (handle == 0) return 0;
167
+
168
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
169
+ if (index >= scanner.count) return 0;
170
+
171
+ const entry = &scanner.entries[index];
172
+ const copy_len = @min(entry.name.len, buf_size - 1);
173
+ @memcpy(buf[0..copy_len], entry.name[0..copy_len]);
174
+ buf[copy_len] = 0;
175
+ return copy_len;
176
+ }
177
+
178
+ /// 指定インデックスのエントリ属性を取得
179
+ export fn core_get_attrs(handle: u64, index: usize, is_dir: *u8, size: *u64, mtime: *i64, executable: *u8, hidden: *u8) i32 {
180
+ if (handle == 0) return -1;
181
+
182
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
183
+ if (index >= scanner.count) return -1;
184
+
185
+ const entry = &scanner.entries[index];
186
+ is_dir.* = if (entry.is_dir) 1 else 0;
187
+ size.* = entry.size;
188
+ mtime.* = entry.mtime;
189
+ executable.* = if (entry.executable) 1 else 0;
190
+ hidden.* = if (entry.hidden) 1 else 0;
191
+ return 0;
192
+ }
193
+
194
+ /// スキャナーを破棄
195
+ export fn core_destroy(handle: u64) void {
196
+ if (handle == 0) return;
197
+
198
+ const scanner = @as(*Scanner, @ptrFromInt(handle));
199
+ scanner.deinit();
200
+ }
201
+
202
+ /// バージョン情報
203
+ export fn core_version() [*:0]const u8 {
204
+ return "3.0.0-handle-based";
205
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.0
4
+ version: 0.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - masisz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-02 00:00:00.000000000 Z
11
+ date: 2026-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -129,22 +129,29 @@ files:
129
129
  - docs/CHANGELOG_v0.30.0.md
130
130
  - docs/CHANGELOG_v0.31.0.md
131
131
  - docs/CHANGELOG_v0.32.0.md
132
+ - docs/CHANGELOG_v0.34.0.md
132
133
  - docs/CHANGELOG_v0.4.0.md
133
134
  - docs/CHANGELOG_v0.5.0.md
134
135
  - docs/CHANGELOG_v0.6.0.md
135
136
  - docs/CHANGELOG_v0.7.0.md
136
137
  - docs/CHANGELOG_v0.8.0.md
137
138
  - docs/CHANGELOG_v0.9.0.md
139
+ - docs/file-preview-optimization-analysis.md
140
+ - docs/file-preview-performance-issue-FIXED.md
138
141
  - info/help.md
139
142
  - info/keybindings.md
140
143
  - info/welcome.md
141
144
  - lib/rufio.rb
142
145
  - lib/rufio/application.rb
146
+ - lib/rufio/async_scanner_fiber.rb
147
+ - lib/rufio/async_scanner_promise.rb
148
+ - lib/rufio/background_command_executor.rb
143
149
  - lib/rufio/bookmark.rb
144
150
  - lib/rufio/bookmark_manager.rb
145
151
  - lib/rufio/color_helper.rb
146
152
  - lib/rufio/command_completion.rb
147
153
  - lib/rufio/command_history.rb
154
+ - lib/rufio/command_logger.rb
148
155
  - lib/rufio/command_mode.rb
149
156
  - lib/rufio/command_mode_ui.rb
150
157
  - lib/rufio/config.rb
@@ -159,6 +166,10 @@ files:
159
166
  - lib/rufio/info_notice.rb
160
167
  - lib/rufio/keybind_handler.rb
161
168
  - lib/rufio/logger.rb
169
+ - lib/rufio/native/rufio_zig.bundle
170
+ - lib/rufio/native_scanner.rb
171
+ - lib/rufio/native_scanner_zig.rb
172
+ - lib/rufio/parallel_scanner.rb
162
173
  - lib/rufio/plugin.rb
163
174
  - lib/rufio/plugin_config.rb
164
175
  - lib/rufio/plugin_manager.rb
@@ -173,6 +184,10 @@ files:
173
184
  - lib/rufio/text_utils.rb
174
185
  - lib/rufio/version.rb
175
186
  - lib/rufio/zoxide_integration.rb
187
+ - lib_zig/rufio_native/Makefile
188
+ - lib_zig/rufio_native/build.zig
189
+ - lib_zig/rufio_native/src/main.zig
190
+ - lib_zig/rufio_native/src/main.zig.sync
176
191
  - publish_gem.zsh
177
192
  - retag.sh
178
193
  - test_delete/test1.txt