@@ -18,6 +18,8 @@ abstract interface class FilesystemCache {
18
18
/// Waits for any pending reads to complete first.
19
19
Future <void > invalidate (Iterable <AssetId > ids);
20
20
21
+ Future <void > flush ();
22
+
21
23
/// Whether [id] exists.
22
24
///
23
25
/// Returns a cached result if available, or caches and returns `ifAbsent()` .
@@ -31,6 +33,12 @@ abstract interface class FilesystemCache {
31
33
required Future <Uint8List > Function () ifAbsent,
32
34
});
33
35
36
+ Future <void > writeAsBytes (
37
+ AssetId id,
38
+ List <int > contents, {
39
+ required Future <void > Function () writer,
40
+ });
41
+
34
42
/// Reads [id] as a `String` .
35
43
///
36
44
/// Returns a cached result if available, or caches and returns `ifAbsent()` .
@@ -39,6 +47,15 @@ abstract interface class FilesystemCache {
39
47
Encoding encoding = utf8,
40
48
required Future <Uint8List > Function () ifAbsent,
41
49
});
50
+
51
+ Future <void > writeAsString (
52
+ AssetId id,
53
+ String contents, {
54
+ Encoding encoding = utf8,
55
+ required Future <void > Function () writer,
56
+ });
57
+
58
+ Future <void > delete (AssetId id, {required Future <void > Function () deleter});
42
59
}
43
60
44
61
/// [FilesystemCache] that always reads from the underlying source.
@@ -48,6 +65,9 @@ class PassthroughFilesystemCache implements FilesystemCache {
48
65
@override
49
66
Future <void > invalidate (Iterable <AssetId > ids) async {}
50
67
68
+ @override
69
+ Future <void > flush () => Future <void >.value ();
70
+
51
71
@override
52
72
Future <bool > exists (
53
73
AssetId id, {
@@ -66,6 +86,25 @@ class PassthroughFilesystemCache implements FilesystemCache {
66
86
Encoding encoding = utf8,
67
87
required Future <Uint8List > Function () ifAbsent,
68
88
}) async => encoding.decode (await ifAbsent ());
89
+
90
+ @override
91
+ Future <void > writeAsBytes (
92
+ AssetId id,
93
+ List <int > contents, {
94
+ required Future <void > Function () writer,
95
+ }) => writer ();
96
+
97
+ @override
98
+ Future <void > writeAsString (
99
+ AssetId id,
100
+ String contents, {
101
+ Encoding encoding = utf8,
102
+ required Future <void > Function () writer,
103
+ }) => writer ();
104
+
105
+ @override
106
+ Future <void > delete (AssetId id, {required Future <void > Function () deleter}) =>
107
+ deleter ();
69
108
}
70
109
71
110
/// [FilesystemCache] that stores data in memory.
@@ -99,6 +138,8 @@ class InMemoryFilesystemCache implements FilesystemCache {
99
138
/// Pending `readAsString` operations.
100
139
final _pendingStringContentCache = < AssetId , Future <String >> {};
101
140
141
+ final _pendingWrites = < AssetId , Future <void > Function ()> {};
142
+
102
143
@override
103
144
Future <void > invalidate (Iterable <AssetId > ids) async {
104
145
// First finish all pending operations, as they will write to the cache.
@@ -113,6 +154,14 @@ class InMemoryFilesystemCache implements FilesystemCache {
113
154
}
114
155
}
115
156
157
+ @override
158
+ Future <void > flush () async {
159
+ for (final write in _pendingWrites.values) {
160
+ await write ();
161
+ }
162
+ _pendingWrites.clear ();
163
+ }
164
+
116
165
@override
117
166
Future <bool > exists (
118
167
AssetId id, {
@@ -127,6 +176,11 @@ class InMemoryFilesystemCache implements FilesystemCache {
127
176
var cached = _bytesContentCache[id];
128
177
if (cached != null ) return Future .value (cached);
129
178
179
+ // TODO(davidmorgan): ensure these can't be evicted.
180
+ if (_pendingWrites.containsKey (id)) {
181
+ throw StateError ('Whoops, should have been cached: $id ' );
182
+ }
183
+
130
184
return _pendingBytesContentCache.putIfAbsent (id, () async {
131
185
final result = await ifAbsent ();
132
186
_bytesContentCache[id] = result;
@@ -149,11 +203,54 @@ class InMemoryFilesystemCache implements FilesystemCache {
149
203
var cached = _stringContentCache[id];
150
204
if (cached != null ) return cached;
151
205
206
+ // TODO(davidmorgan): ensure these can't be evicted.
207
+ if (_pendingWrites.containsKey (id)) {
208
+ throw StateError ('Whoops, should have been cached: $id ' );
209
+ }
210
+
152
211
return _pendingStringContentCache.putIfAbsent (id, () async {
153
212
final bytes = await ifAbsent ();
154
213
final result = _stringContentCache[id] = utf8.decode (bytes);
155
214
unawaited (_pendingStringContentCache.remove (id));
156
215
return result;
157
216
});
158
217
}
218
+
219
+ @override
220
+ Future <void > writeAsBytes (
221
+ AssetId id,
222
+ List <int > contents, {
223
+ required Future <void > Function () writer,
224
+ }) {
225
+ _bytesContentCache[id] =
226
+ contents is Uint8List ? contents : Uint8List .fromList (contents);
227
+ _stringContentCache[id] = utf8.decode (contents);
228
+ _canReadCache[id] = Future .value (true );
229
+
230
+ _pendingWrites[id] = writer;
231
+ return Future .value (null );
232
+ }
233
+
234
+ @override
235
+ Future <void > writeAsString (
236
+ AssetId id,
237
+ String contents, {
238
+ Encoding encoding = utf8,
239
+ required Future <void > Function () writer,
240
+ }) {
241
+ // TODO: encoding?
242
+ _stringContentCache[id] = contents;
243
+ _bytesContentCache[id] = utf8.encode (contents);
244
+ _canReadCache[id] = Future .value (true );
245
+
246
+ _pendingWrites[id] = writer;
247
+ return Future .value (null );
248
+ }
249
+
250
+ @override
251
+ Future <void > delete (AssetId id, {required Future <void > Function () deleter}) {
252
+ _pendingWrites[id] = deleter;
253
+ _canReadCache[id] = Future .value (false );
254
+ return Future .value (null );
255
+ }
159
256
}
0 commit comments