Skip to content

Commit f86b01b

Browse files
committed
Initial native implementation of FS methods
1 parent 4715892 commit f86b01b

34 files changed

+2481
-839
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ app.*.map.json
4444
/android/app/debug
4545
/android/app/profile
4646
/android/app/release
47+
48+
**/zig-out
49+
**/.zig-cache

.vscode/launch.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
8+
{
9+
"name": "files",
10+
"request": "launch",
11+
"type": "dart",
12+
"args": ["--enable-experiment=native-assets"]
13+
},
14+
{
15+
"name": "files (profile mode)",
16+
"request": "launch",
17+
"type": "dart",
18+
"flutterMode": "profile",
19+
"args": ["--enable-experiment=native-assets"]
20+
},
21+
{
22+
"name": "files (release mode)",
23+
"request": "launch",
24+
"type": "dart",
25+
"flutterMode": "release",
26+
"args": ["--enable-experiment=native-assets"]
27+
}
28+
]
29+
}

hook/build.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'package:code_assets/code_assets.dart';
2+
import 'package:hooks/hooks.dart';
3+
4+
void main(List<String> args) async {
5+
await build(args, (input, output) async {
6+
final targetOS = input.config.code.targetOS;
7+
if (targetOS != OS.linux) return;
8+
9+
output.assets.code.add(
10+
CodeAsset(
11+
package: input.packageName,
12+
name: 'libfs.dart',
13+
linkMode: DynamicLoadingSystem(Uri.file('src/zig-out/lib/libfs.so')),
14+
),
15+
);
16+
});
17+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#
2+
# Generated file, do not edit.
3+
#
4+
5+
import lldb
6+
7+
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
8+
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
9+
base = frame.register["x0"].GetValueAsAddress()
10+
page_len = frame.register["x1"].GetValueAsUnsigned()
11+
12+
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
13+
# first page to see if handled it correctly. This makes diagnosing
14+
# misconfiguration (e.g. missing breakpoint) easier.
15+
data = bytearray(page_len)
16+
data[0:8] = b'IHELPED!'
17+
18+
error = lldb.SBError()
19+
frame.GetThread().GetProcess().WriteMemory(base, data, error)
20+
if not error.Success():
21+
print(f'Failed to write into {base}[+{page_len}]', error)
22+
return
23+
24+
def __lldb_init_module(debugger: lldb.SBDebugger, _):
25+
target = debugger.GetDummyTarget()
26+
# Caveat: must use BreakpointCreateByRegEx here and not
27+
# BreakpointCreateByName. For some reasons callback function does not
28+
# get carried over from dummy target for the later.
29+
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
30+
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
31+
bp.SetAutoContinue(True)
32+
print("-- LLDB integration loaded --")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#
2+
# Generated file, do not edit.
3+
#
4+
5+
command script import --relative-to-command-file flutter_lldb_helper.py

lib/backend/database/helper.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import 'dart:io';
2-
31
import 'package:files/backend/database/model.dart';
2+
import 'package:files/backend/fs.dart' as fs;
43
import 'package:files/backend/providers.dart';
4+
import 'package:isar_community/isar.dart';
55

66
class EntityStatCacheHelper {
77
Future<EntityStat> get(String path) async {
88
final stat = isar.entityStats.where().pathEqualTo(path).findFirstSync();
99

1010
if (stat == null) {
11-
final fetchedStat = EntityStat.fromStat(
11+
final file = fs.File.fromPath(path);
12+
final fetchedStat = EntityStat.fromFileInfo(
1213
path,
13-
await FileStat.stat(path),
14+
await file.queryInfo().result,
1415
);
1516
await set(fetchedStat);
1617
return fetchedStat;

lib/backend/database/model.dart

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,52 @@
1-
import 'dart:io';
2-
1+
import 'package:files/backend/fs.dart' as fs;
32
import 'package:files/backend/providers.dart';
43
import 'package:flutter/foundation.dart';
5-
import 'package:isar/isar.dart';
6-
7-
export 'package:isar/isar.dart';
4+
import 'package:isar_community/isar.dart';
85

96
part 'model.g.dart';
107

8+
typedef _FileInfoSnapshot = ({
9+
DateTime changed,
10+
DateTime modified,
11+
DateTime accessed,
12+
EntityType type,
13+
int mode,
14+
int size,
15+
});
16+
17+
_FileInfoSnapshot _getSnapshotForInfo(fs.FileInfo info) {
18+
final attributes = info.listAttributes()!;
19+
20+
assert(attributes.contains('standard::type'));
21+
// assert(attributes.contains('time::modified'));
22+
// assert(attributes.contains('time::access'));
23+
// assert(attributes.contains('time::created'));
24+
assert(attributes.contains('standard::size'));
25+
26+
final defaultTime = DateTime.fromMillisecondsSinceEpoch(0);
27+
28+
return (
29+
changed: info.getCreationTime() ?? defaultTime,
30+
modified: info.getModificationTime() ?? defaultTime,
31+
accessed: info.getAccessTime() ?? defaultTime,
32+
type: switch (info.getFileType()) {
33+
1 || 4 || 5 => EntityType.file,
34+
2 || 6 => EntityType.directory,
35+
3 => EntityType.link,
36+
final t => throw ArgumentError.value(t),
37+
},
38+
mode: 0, // TODO
39+
size: info.getSize(),
40+
);
41+
}
42+
1143
@Collection()
1244
class EntityStat with ChangeNotifier {
1345
EntityStat();
1446

1547
EntityStat.fastInit({
1648
required this.path,
49+
required this.info,
1750
required this.changed,
1851
required this.modified,
1952
required this.accessed,
@@ -22,21 +55,29 @@ class EntityStat with ChangeNotifier {
2255
required this.size,
2356
});
2457

25-
EntityStat.fromStat(String path, FileStat stat)
26-
: this.fastInit(
27-
path: path,
28-
changed: stat.changed,
29-
modified: stat.modified,
30-
accessed: stat.accessed,
31-
type: EntityType.fromDartIo(stat.type),
32-
mode: stat.mode,
33-
size: stat.size,
34-
);
58+
factory EntityStat.fromFileInfo(String path, fs.FileInfo info) {
59+
final snapshot = _getSnapshotForInfo(info);
60+
61+
return EntityStat.fastInit(
62+
path: path,
63+
info: info,
64+
changed: snapshot.changed,
65+
modified: snapshot.modified,
66+
accessed: snapshot.accessed,
67+
type: snapshot.type,
68+
mode: snapshot.mode,
69+
size: snapshot.size,
70+
);
71+
}
72+
3573
Id? id;
3674

3775
@Index(unique: true, type: IndexType.hash)
3876
late String path;
3977

78+
@Ignore()
79+
late fs.FileInfo info;
80+
4081
late DateTime changed;
4182
late DateTime modified;
4283
late DateTime accessed;
@@ -47,60 +88,30 @@ class EntityStat with ChangeNotifier {
4788
late int size;
4889

4990
Future<void> fetchUpdate() async {
50-
final ioStat = await FileStat.stat(path);
51-
52-
if (!_statIdentical(ioStat)) {
53-
changed = ioStat.changed;
54-
modified = ioStat.modified;
55-
accessed = ioStat.accessed;
56-
type = EntityType.fromDartIo(ioStat.type);
57-
mode = ioStat.mode;
58-
size = ioStat.size;
91+
final file = fs.File.fromPath(path);
92+
final info = await file.queryInfo().result;
93+
final snapshot = _getSnapshotForInfo(info);
94+
95+
if (!_infoIdentical(snapshot)) {
96+
changed = snapshot.changed;
97+
modified = snapshot.modified;
98+
accessed = snapshot.accessed;
99+
type = snapshot.type;
100+
mode = snapshot.mode;
101+
size = snapshot.size;
59102
await helper.set(this);
60103
notifyListeners();
61104
}
62105
}
63106

64-
bool _statIdentical(FileStat other) {
107+
bool _infoIdentical(_FileInfoSnapshot other) {
65108
return changed == other.changed &&
66109
modified == other.modified &&
67110
accessed == other.accessed &&
68-
type == EntityType.fromDartIo(other.type) &&
111+
type == other.type &&
69112
mode == other.mode &&
70113
size == other.size;
71114
}
72115
}
73116

74-
enum EntityType {
75-
file,
76-
directory,
77-
link,
78-
notFound;
79-
80-
static EntityType fromDartIo(FileSystemEntityType type) {
81-
switch (type) {
82-
case FileSystemEntityType.file:
83-
return EntityType.file;
84-
case FileSystemEntityType.directory:
85-
return EntityType.directory;
86-
case FileSystemEntityType.link:
87-
return EntityType.link;
88-
case FileSystemEntityType.notFound:
89-
default:
90-
return EntityType.notFound;
91-
}
92-
}
93-
94-
FileSystemEntityType get toDartIo {
95-
switch (this) {
96-
case EntityType.file:
97-
return FileSystemEntityType.file;
98-
case EntityType.directory:
99-
return FileSystemEntityType.directory;
100-
case EntityType.link:
101-
return FileSystemEntityType.link;
102-
case EntityType.notFound:
103-
return FileSystemEntityType.notFound;
104-
}
105-
}
106-
}
117+
enum EntityType { file, directory, link, notFound }

0 commit comments

Comments
 (0)