Skip to content

Commit a7d5702

Browse files
author
LTRData
committed
Cache for NtfsFileSystem.UsedSpace
* Significantly reduces delays when mounting as virtual file system.
1 parent 37f4d15 commit a7d5702

File tree

2 files changed

+58
-30
lines changed

2 files changed

+58
-30
lines changed

Library/DiscUtils.Ntfs/ClusterBitmap.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020
// DEALINGS IN THE SOFTWARE.
2121
//
2222

23+
using DiscUtils.Streams;
2324
using System;
25+
using System.Buffers;
2426
using System.Collections.Generic;
2527
using System.IO;
2628
using System.Threading;
2729
using System.Threading.Tasks;
28-
using DiscUtils.Streams;
2930

3031
namespace DiscUtils.Ntfs;
3132

@@ -36,6 +37,8 @@ internal class ClusterBitmap : IDisposable
3637

3738
private long _nextDataCluster;
3839

40+
private long? _usedClusters;
41+
3942
public ClusterBitmap(File file)
4043
{
4144
_file = file;
@@ -47,6 +50,35 @@ public ClusterBitmap(File file)
4750

4851
internal Bitmap Bitmap { get; private set; }
4952

53+
public long GetUsedClustersCount()
54+
{
55+
if (_usedClusters is null)
56+
{
57+
long usedClusters = 0;
58+
long processed = 0;
59+
60+
var bufferSize = (int)Math.Min(4 * Sizes.OneMiB, Bitmap.Size);
61+
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
62+
try
63+
{
64+
while (processed < Bitmap.Size)
65+
{
66+
var count = Bitmap.GetBytes(processed, buffer, 0, bufferSize);
67+
usedClusters += BitCounter.Count(buffer, 0, count);
68+
processed += count;
69+
}
70+
}
71+
finally
72+
{
73+
ArrayPool<byte>.Shared.Return(buffer);
74+
}
75+
76+
_usedClusters = usedClusters;
77+
}
78+
79+
return _usedClusters.Value;
80+
}
81+
5082
public void Dispose()
5183
{
5284
if (Bitmap != null)
@@ -142,6 +174,8 @@ public List<Range<long, long>> AllocateClusters(long count, long proposedStart,
142174
_fragmentedDiskMode = numFound / result.Count < 4;
143175
}
144176

177+
_usedClusters += total;
178+
145179
return result;
146180
}
147181

@@ -232,19 +266,25 @@ public async ValueTask<List<Range<long, long>>> AllocateClustersAsync(long count
232266
_fragmentedDiskMode = numFound / result.Count < 4;
233267
}
234268

269+
_usedClusters += total;
270+
235271
return result;
236272
}
237273

238274
internal void MarkAllocated(long first, long count)
239275
{
240276
Bitmap.MarkPresentRange(first, count);
277+
278+
_usedClusters += count;
241279
}
242280

243281
internal void FreeClusters(IEnumerable<Range<long, long>> runs)
244282
{
245283
foreach (var run in runs)
246284
{
247285
Bitmap.MarkAbsentRange(run.Offset, run.Count);
286+
287+
_usedClusters -= run.Count;
248288
}
249289
}
250290

@@ -253,17 +293,23 @@ internal async ValueTask FreeClustersAsync(IEnumerable<Range<long, long>> runs,
253293
foreach (var run in runs)
254294
{
255295
await Bitmap.MarkAbsentRangeAsync(run.Offset, run.Count, cancellationToken).ConfigureAwait(false);
296+
297+
_usedClusters -= run.Count;
256298
}
257299
}
258300

259301
internal void FreeClusters(Range<long, long> run)
260302
{
261303
Bitmap.MarkAbsentRange(run.Offset, run.Count);
304+
305+
_usedClusters -= run.Count;
262306
}
263307

264-
internal ValueTask FreeClustersAsync(Range<long, long> run, CancellationToken cancellationToken)
308+
internal async ValueTask FreeClustersAsync(Range<long, long> run, CancellationToken cancellationToken)
265309
{
266-
return Bitmap.MarkAbsentRangeAsync(run.Offset, run.Count, cancellationToken);
310+
await Bitmap.MarkAbsentRangeAsync(run.Offset, run.Count, cancellationToken).ConfigureAwait(false);
311+
312+
_usedClusters -= run.Count;
267313
}
268314

269315
/// <summary>
@@ -278,6 +324,8 @@ internal void SetTotalClusters(long numClusters)
278324
var actualClusters = Bitmap.SetTotalEntries(numClusters);
279325
if (actualClusters != numClusters)
280326
{
327+
_usedClusters = null;
328+
281329
MarkAllocated(numClusters, actualClusters - numClusters);
282330
}
283331
}
@@ -295,6 +343,9 @@ private long ExtendRun(long count, List<Range<long, long>> result, long start, l
295343
if (numFound > 0)
296344
{
297345
Bitmap.MarkPresentRange(start, numFound);
346+
347+
_usedClusters += numFound;
348+
298349
result.Add(new Range<long, long>(start, numFound));
299350
}
300351

@@ -314,6 +365,9 @@ private async ValueTask<long> ExtendRunAsync(long count, List<Range<long, long>>
314365
if (numFound > 0)
315366
{
316367
await Bitmap.MarkPresentRangeAsync(start, numFound, cancellationToken).ConfigureAwait(false);
368+
369+
_usedClusters += numFound;
370+
317371
result.Add(new Range<long, long>(start, numFound));
318372
}
319373

Library/DiscUtils.Ntfs/NtfsFileSystem.cs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2632,33 +2632,7 @@ internal void ForgetFile(File file)
26322632
/// <summary>
26332633
/// Used space of the Filesystem in bytes
26342634
/// </summary>
2635-
public override long UsedSpace
2636-
{
2637-
get
2638-
{
2639-
long usedCluster = 0;
2640-
var bitmap = _context.ClusterBitmap.Bitmap;
2641-
var processed = 0L;
2642-
2643-
var bufferSize = (int)Math.Min(4 * Sizes.OneMiB, bitmap.Size);
2644-
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
2645-
try
2646-
{
2647-
while (processed < bitmap.Size)
2648-
{
2649-
var count = bitmap.GetBytes(processed, buffer, 0, bufferSize);
2650-
usedCluster += BitCounter.Count(buffer, 0, count);
2651-
processed += count;
2652-
}
2653-
}
2654-
finally
2655-
{
2656-
ArrayPool<byte>.Shared.Return(buffer);
2657-
}
2658-
2659-
return usedCluster * ClusterSize;
2660-
}
2661-
}
2635+
public override long UsedSpace => _context.ClusterBitmap.GetUsedClustersCount() * ClusterSize;
26622636

26632637
/// <summary>
26642638
/// Available space of the Filesystem in bytes

0 commit comments

Comments
 (0)