1
1
using System ;
2
+ using System . Collections . Concurrent ;
2
3
using System . Collections . Immutable ;
3
4
using System . IO ;
4
5
using System . Reflection ;
@@ -23,7 +24,8 @@ public StorageService(IStorageServiceConfig configData)
23
24
{
24
25
_configData = configData ;
25
26
}
26
-
27
+
28
+ private readonly ConcurrentDictionary < string , OneOf . OneOf < byte [ ] , string , XDocument > > _fsCache = new ( ) ;
27
29
private readonly IStorageServiceConfig _configData ;
28
30
private readonly string _runLocation = Path . GetDirectoryName ( Assembly . GetEntryAssembly ( ) ! . Location . CleanUpPath ( ) ) ;
29
31
@@ -34,6 +36,18 @@ public void Dispose()
34
36
ModUtils . Threading . SetBool ( ref _isDisposed , true ) ;
35
37
}
36
38
39
+ public void PurgeCache ( )
40
+ {
41
+ _fsCache . Clear ( ) ;
42
+ }
43
+
44
+ private int _useCaching ;
45
+ public bool UseCaching
46
+ {
47
+ get => ModUtils . Threading . GetBool ( ref _useCaching ) ;
48
+ set => ModUtils . Threading . SetBool ( ref _useCaching , value ) ;
49
+ }
50
+
37
51
public FluentResults . Result < XDocument > LoadLocalXml ( ContentPackage package , string localFilePath ) =>
38
52
GetAbsFromLocal ( package , localFilePath ) is var r && r is { IsSuccess : true , Value : not null }
39
53
? TryLoadXml ( r . Value ) : r . ToResult ( ) ;
@@ -144,8 +158,11 @@ public FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPa
144
158
if ( localFilePaths . IsDefaultOrEmpty )
145
159
return ImmutableArray < ( string , FluentResults . Result < XDocument > ) > . Empty ;
146
160
var builder = ImmutableArray . CreateBuilder < ( string , FluentResults . Result < XDocument > ) > ( localFilePaths . Length ) ;
147
- foreach ( var path in localFilePaths )
161
+
162
+ await localFilePaths . ParallelForEachAsync ( async path =>
163
+ {
148
164
builder . Add ( ( path , await LoadPackageXmlAsync ( package , path ) ) ) ;
165
+ } , maxDegreeOfParallelism : 2 ) ;
149
166
return builder . MoveToImmutable ( ) ;
150
167
}
151
168
@@ -155,8 +172,10 @@ public FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPa
155
172
if ( localFilePaths . IsDefaultOrEmpty )
156
173
return ImmutableArray < ( string , FluentResults . Result < byte [ ] > ) > . Empty ;
157
174
var builder = ImmutableArray . CreateBuilder < ( string , FluentResults . Result < byte [ ] > ) > ( localFilePaths . Length ) ;
158
- foreach ( var path in localFilePaths )
175
+ await localFilePaths . ParallelForEachAsync ( async path =>
176
+ {
159
177
builder . Add ( ( path , await LoadPackageBinaryAsync ( package , path ) ) ) ;
178
+ } , maxDegreeOfParallelism : 2 ) ;
160
179
return builder . MoveToImmutable ( ) ;
161
180
}
162
181
@@ -166,8 +185,10 @@ public FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPa
166
185
if ( localFilePaths . IsDefaultOrEmpty )
167
186
return ImmutableArray < ( string , FluentResults . Result < string > ) > . Empty ;
168
187
var builder = ImmutableArray . CreateBuilder < ( string , FluentResults . Result < string > ) > ( localFilePaths . Length ) ;
169
- foreach ( var path in localFilePaths )
188
+ await localFilePaths . ParallelForEachAsync ( async path =>
189
+ {
170
190
builder . Add ( ( path , await LoadPackageTextAsync ( package , path ) ) ) ;
191
+ } , maxDegreeOfParallelism : 2 ) ;
171
192
return builder . MoveToImmutable ( ) ;
172
193
}
173
194
@@ -188,23 +209,39 @@ public FluentResults.Result<XDocument> TryLoadXml(string filePath, Encoding enco
188
209
public FluentResults . Result < string > TryLoadText ( string filePath , Encoding encoding = null )
189
210
{
190
211
( ( IService ) this ) . CheckDisposed ( ) ;
212
+ if ( UseCaching && _fsCache . TryGetValue ( filePath , out var result )
213
+ && result . TryPickT1 ( out var cachedVal , out _ ) )
214
+ {
215
+ return FluentResults . Result . Ok ( cachedVal ) ;
216
+ }
217
+
191
218
return IOExceptionsOperationRunner ( nameof ( TryLoadText ) , filePath , ( ) =>
192
219
{
193
220
var fp = filePath . CleanUpPath ( ) ;
194
221
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
195
222
var fileText = encoding is null ? System . IO . File . ReadAllText ( fp ) : System . IO . File . ReadAllText ( fp , encoding ) ;
223
+ if ( UseCaching )
224
+ _fsCache [ filePath ] = fileText ;
196
225
return new FluentResults . Result < string > ( ) . WithSuccess ( $ "Loaded file successfully") . WithValue ( fileText ) ;
197
226
} ) ;
198
227
}
199
228
200
229
public FluentResults . Result < byte [ ] > TryLoadBinary ( string filePath )
201
230
{
202
231
( ( IService ) this ) . CheckDisposed ( ) ;
232
+ if ( UseCaching && _fsCache . TryGetValue ( filePath , out var result )
233
+ && result . TryPickT0 ( out var cachedVal , out _ ) )
234
+ {
235
+ return FluentResults . Result . Ok ( cachedVal ) ;
236
+ }
237
+
203
238
return IOExceptionsOperationRunner ( nameof ( TryLoadBinary ) , filePath , ( ) =>
204
239
{
205
240
var fp = filePath . CleanUpPath ( ) ;
206
241
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
207
242
var fileData = System . IO . File . ReadAllBytes ( fp ) ;
243
+ if ( UseCaching )
244
+ _fsCache [ filePath ] = fileData ;
208
245
return new FluentResults . Result < byte [ ] > ( ) . WithSuccess ( $ "Loaded file successfully") . WithValue ( fileData ) ;
209
246
} ) ;
210
247
}
@@ -220,13 +257,14 @@ public FluentResults.Result TrySaveText(string filePath, in string text, Encodin
220
257
. WithMetadata ( MetadataType . ExceptionObject , this )
221
258
. WithMetadata ( MetadataType . Sources , filePath ) ) ;
222
259
}
223
-
224
260
string t = text ; //copy
225
261
return IOExceptionsOperationRunner ( nameof ( TrySaveText ) , filePath , ( ) =>
226
262
{
227
263
var fp = filePath . CleanUpPath ( ) ;
228
264
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
229
265
System . IO . File . WriteAllText ( fp , t , encoding ) ;
266
+ if ( UseCaching )
267
+ _fsCache [ filePath ] = t ;
230
268
return new FluentResults . Result ( ) . WithSuccess ( $ "Saved to file successfully") ;
231
269
} ) ;
232
270
}
@@ -248,6 +286,8 @@ public FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes)
248
286
var fp = filePath . CleanUpPath ( ) ;
249
287
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
250
288
System . IO . File . WriteAllBytes ( fp , b ) ;
289
+ if ( UseCaching )
290
+ _fsCache [ filePath ] = b ;
251
291
return new FluentResults . Result ( ) . WithSuccess ( $ "Saved to file successfully") ;
252
292
} ) ;
253
293
}
@@ -279,10 +319,16 @@ public FluentResults.Result<bool> DirectoryExists(string directoryPath)
279
319
280
320
public async Task < FluentResults . Result < XDocument > > TryLoadXmlAsync ( string filePath , Encoding encoding = null )
281
321
{
322
+ if ( UseCaching && _fsCache . TryGetValue ( filePath , out var cachedVal )
323
+ && cachedVal . TryPickT2 ( out var cachedDoc , out _ ) )
324
+ return FluentResults . Result . Ok ( cachedDoc ) ;
282
325
try
283
326
{
284
327
await using var fs = new FileStream ( filePath , FileMode . Open , FileAccess . Read ) ;
285
- return await XDocument . LoadAsync ( fs , LoadOptions . PreserveWhitespace , CancellationToken . None ) ;
328
+ var doc = await XDocument . LoadAsync ( fs , LoadOptions . PreserveWhitespace , CancellationToken . None ) ;
329
+ if ( UseCaching )
330
+ _fsCache [ filePath ] = doc ;
331
+ return FluentResults . Result . Ok ( doc ) ;
286
332
}
287
333
catch ( Exception e )
288
334
{
@@ -293,17 +339,30 @@ public FluentResults.Result<bool> DirectoryExists(string directoryPath)
293
339
public async Task < FluentResults . Result < string > > TryLoadTextAsync ( string filePath , Encoding encoding = null )
294
340
{
295
341
( ( IService ) this ) . CheckDisposed ( ) ;
342
+ if ( UseCaching && _fsCache . TryGetValue ( filePath , out var cachedVal )
343
+ && cachedVal . TryPickT1 ( out var cachedTxt , out _ ) )
344
+ return FluentResults . Result . Ok ( cachedTxt ) ;
345
+
296
346
return await IOExceptionsOperationRunnerAsync < string > ( nameof ( TryLoadTextAsync ) , filePath , async ( ) =>
297
347
{
298
348
var fp = filePath . CleanUpPath ( ) ;
299
349
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
300
- return await System . IO . File . ReadAllTextAsync ( fp ) ;
350
+ var txt = await System . IO . File . ReadAllTextAsync ( fp ) ;
351
+ if ( UseCaching )
352
+ _fsCache [ filePath ] = txt ;
353
+ return FluentResults . Result . Ok ( txt ) ;
301
354
} ) ;
302
355
}
303
356
304
357
public async Task < FluentResults . Result < byte [ ] > > TryLoadBinaryAsync ( string filePath )
305
358
{
306
359
( ( IService ) this ) . CheckDisposed ( ) ;
360
+ if ( UseCaching && _fsCache . TryGetValue ( filePath , out var cachedVal )
361
+ && cachedVal . TryPickT0 ( out var cachedBin , out _ ) )
362
+ {
363
+ return cachedBin ;
364
+ }
365
+
307
366
return await IOExceptionsOperationRunnerAsync < byte [ ] > ( nameof ( TryLoadTextAsync ) , filePath , async ( ) =>
308
367
{
309
368
var fp = filePath . CleanUpPath ( ) ;
@@ -330,6 +389,8 @@ public FluentResults.Result<bool> DirectoryExists(string directoryPath)
330
389
var fp = filePath . CleanUpPath ( ) ;
331
390
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
332
391
await System . IO . File . WriteAllTextAsync ( fp , t , encoding ) ;
392
+ if ( UseCaching )
393
+ _fsCache [ filePath ] = t ;
333
394
return new FluentResults . Result ( ) . WithSuccess ( $ "Saved to file successfully") ;
334
395
} ) ;
335
396
}
@@ -351,6 +412,8 @@ public FluentResults.Result<bool> DirectoryExists(string directoryPath)
351
412
var fp = filePath . CleanUpPath ( ) ;
352
413
fp = System . IO . Path . IsPathRooted ( fp ) ? fp : System . IO . Path . GetFullPath ( fp ) ;
353
414
await System . IO . File . WriteAllBytesAsync ( fp , b ) ;
415
+ if ( UseCaching )
416
+ _fsCache [ filePath ] = b ;
354
417
return new FluentResults . Result ( ) . WithSuccess ( $ "Saved to file successfully") ;
355
418
} ) ;
356
419
}
0 commit comments