anshdadhich's picture
initial commit
1c4658f
using FastSeek.Core.Mft;
namespace FastSeek.Core.Index;
public sealed class IndexEntry
{
public ulong FileRef;
public ulong ParentRef;
public required string Name;
public required string NameLower;
public byte Flags;
public bool IsDir => (Flags & 1) != 0;
public FileKind Kind => IsDir ? FileKind.Directory : FileKind.File;
}
public sealed class CachedEntry
{
public ulong FileRef { get; set; }
public ulong ParentRef { get; set; }
public required string Name { get; set; }
public FileKind Kind { get; set; }
}
public sealed class CacheData
{
public List<CachedEntry> Entries { get; set; } = new();
public string DriveRoot { get; set; } = string.Empty;
public List<JournalCheckpoint> Checkpoints { get; set; } = new();
}
public sealed class IndexStore
{
public List<IndexEntry> Entries { get; } = new();
public List<(ulong fileRef, int idx)> RefLookup { get; } = new();
public string DriveRoot { get; set; } = string.Empty;
public List<JournalCheckpoint> Checkpoints { get; set; } = new();
public string Name(IndexEntry e) => e.Name;
public string NameLower(IndexEntry e) => e.NameLower;
public int? LookupIdx(ulong fileRef)
{
var lo = 0;
var hi = RefLookup.Count - 1;
while (lo <= hi)
{
var mid = lo + ((hi - lo) >> 1);
var cmp = RefLookup[mid].fileRef.CompareTo(fileRef);
if (cmp == 0) return RefLookup[mid].idx;
if (cmp < 0) lo = mid + 1; else hi = mid - 1;
}
return null;
}
internal void PopulateFromScan(ScanResult scan, string driveRoot)
{
DriveRoot = driveRoot;
var span = System.Runtime.InteropServices.CollectionsMarshal.AsSpan(scan.NameData);
Entries.Capacity = Math.Max(Entries.Capacity, Entries.Count + scan.Records.Count);
foreach (var r in scan.Records)
{
var name = new string(span.Slice((int)r.NameOff, r.NameLen));
AddEntry(r.FileRef, r.ParentRef, name, r.IsDir ? FileKind.Directory : FileKind.File);
}
}
public void FinalizeStore()
{
Entries.Sort(static (a, b) => string.CompareOrdinal(a.NameLower, b.NameLower));
RebuildRefLookup();
}
public void Insert(FileRecord record) => AddSorted(record.FileRef, record.ParentRef, record.Name, record.Kind);
public void Remove(ulong fileRef) { Entries.RemoveAll(e => e.FileRef == fileRef); RebuildRefLookup(); }
public void Rename(ulong oldRef, FileRecord r) { Remove(oldRef); Insert(r); }
public void ApplyMove(ulong fileRef, ulong newParentRef, string name, FileKind kind) { Remove(fileRef); Insert(new FileRecord { FileRef = fileRef, ParentRef = newParentRef, Name = name, Kind = kind }); }
public int Len() => Entries.Count;
public CacheData ToCache() => new()
{
DriveRoot = DriveRoot,
Checkpoints = Checkpoints,
Entries = Entries.Select(e => new CachedEntry { FileRef = e.FileRef, ParentRef = e.ParentRef, Name = e.Name, Kind = e.Kind }).ToList()
};
public static IndexStore FromCache(CacheData cache)
{
var s = new IndexStore { DriveRoot = cache.DriveRoot, Checkpoints = cache.Checkpoints };
s.Entries.Capacity = cache.Entries.Count;
foreach (var c in cache.Entries) s.AddEntry(c.FileRef, c.ParentRef, c.Name, c.Kind);
s.RebuildRefLookup();
return s;
}
private void AddSorted(ulong fileRef, ulong parentRef, string name, FileKind kind)
{
var lower = name.ToLowerInvariant();
var pos = Entries.BinarySearch(new IndexEntry { FileRef = fileRef, ParentRef = parentRef, Name = name, NameLower = lower, Flags = kind == FileKind.Directory ? (byte)1 : (byte)0 },
Comparer<IndexEntry>.Create(static (a, b) => string.CompareOrdinal(a.NameLower, b.NameLower)));
if (pos < 0) pos = ~pos;
Entries.Insert(pos, new IndexEntry { FileRef = fileRef, ParentRef = parentRef, Name = name, NameLower = lower, Flags = kind == FileKind.Directory ? (byte)1 : (byte)0 });
RebuildRefLookup();
}
private void AddEntry(ulong fileRef, ulong parentRef, string name, FileKind kind)
{
Entries.Add(new IndexEntry
{
FileRef = fileRef,
ParentRef = parentRef,
Name = name,
NameLower = name.ToLowerInvariant(),
Flags = kind == FileKind.Directory ? (byte)1 : (byte)0
});
}
private void RebuildRefLookup()
{
RefLookup.Clear();
RefLookup.Capacity = Math.Max(RefLookup.Capacity, Entries.Count);
for (var i = 0; i < Entries.Count; i++) RefLookup.Add((Entries[i].FileRef, i));
RefLookup.Sort(static (a, b) => a.fileRef.CompareTo(b.fileRef));
}
}