anshdadhich commited on
Commit
2cfa635
·
verified ·
1 Parent(s): 8304113

Upload FastSeekWpf/Core/IndexStore.cs

Browse files
Files changed (1) hide show
  1. FastSeekWpf/Core/IndexStore.cs +297 -0
FastSeekWpf/Core/IndexStore.cs ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Buffers;
3
+ using System.Collections.Generic;
4
+ using System.IO;
5
+ using System.Linq;
6
+ using System.Runtime.InteropServices;
7
+ using System.Text;
8
+ using System.Text.Json;
9
+ using System.Text.Json.Serialization;
10
+
11
+ namespace FastSeekWpf.Core;
12
+
13
+ [Serializable]
14
+ public class CachedEntry
15
+ {
16
+ public ulong FileRef { get; set; }
17
+ public ulong ParentRef { get; set; }
18
+ public string Name { get; set; } = string.Empty;
19
+ public FileKind Kind { get; set; }
20
+ }
21
+
22
+ [Serializable]
23
+ public class CacheData
24
+ {
25
+ public List<CachedEntry> Entries { get; set; } = new();
26
+ public string DriveRoot { get; set; } = string.Empty;
27
+ public List<JournalCheckpoint> Checkpoints { get; set; } = new();
28
+ }
29
+
30
+ public struct IndexEntry
31
+ {
32
+ public ulong FileRef;
33
+ public ulong ParentRef;
34
+ public uint NameOff;
35
+ public uint NameLowerOff;
36
+ public ushort NameLen;
37
+ public ushort NameLowerLen;
38
+ public byte Flags;
39
+
40
+ public readonly bool IsDir => (Flags & 1) != 0;
41
+ public readonly FileKind Kind => IsDir ? FileKind.Directory : FileKind.File;
42
+ }
43
+
44
+ public class IndexStore
45
+ {
46
+ public List<IndexEntry> Entries = new();
47
+ public byte[] NameArena = Array.Empty<byte>();
48
+ public byte[] NameLowerArena = Array.Empty<byte>();
49
+ public List<(ulong fileRef, uint idx)> RefLookup = new();
50
+ public string DriveRoot = string.Empty;
51
+ public List<JournalCheckpoint> Checkpoints = new();
52
+
53
+ public int Count => Entries.Count;
54
+
55
+ public string Name(IndexEntry e)
56
+ {
57
+ return Encoding.UTF8.GetString(NameArena, (int)e.NameOff, e.NameLen);
58
+ }
59
+
60
+ public string NameLower(IndexEntry e)
61
+ {
62
+ return Encoding.UTF8.GetString(NameLowerArena, (int)e.NameLowerOff, e.NameLowerLen);
63
+ }
64
+
65
+ public uint? LookupIdx(ulong fileRef)
66
+ {
67
+ int lo = 0, hi = RefLookup.Count - 1;
68
+ while (lo <= hi)
69
+ {
70
+ int mid = (lo + hi) >>> 1;
71
+ var (r, _) = RefLookup[mid];
72
+ if (r == fileRef) return RefLookup[mid].idx;
73
+ if (r < fileRef) lo = mid + 1;
74
+ else hi = mid - 1;
75
+ }
76
+ return null;
77
+ }
78
+
79
+ private void RebuildRefLookup()
80
+ {
81
+ RefLookup.Clear();
82
+ RefLookup.Capacity = Entries.Count;
83
+ for (uint i = 0; i < Entries.Count; i++)
84
+ RefLookup.Add((Entries[(int)i].FileRef, i));
85
+ RefLookup.Sort((a, b) => a.fileRef.CompareTo(b.fileRef));
86
+ }
87
+
88
+ public void PopulateFromScan(ScanResult scan, string driveRoot)
89
+ {
90
+ DriveRoot = driveRoot;
91
+ int count = scan.Records.Count;
92
+ Entries.Capacity = count;
93
+ var nameArenaList = new List<byte>(count * 30);
94
+ var nameLowerList = new List<byte>(count * 30);
95
+
96
+ foreach (var r in scan.Records)
97
+ {
98
+ var nameChars = scan.NameData.Slice((int)r.NameOff, r.NameLen);
99
+ string name = new string(nameChars);
100
+ string nameLower = name.ToLowerInvariant();
101
+
102
+ byte[] nameBytes = Encoding.UTF8.GetBytes(name);
103
+ byte[] lowerBytes = Encoding.UTF8.GetBytes(nameLower);
104
+
105
+ uint nOff = (uint)nameArenaList.Count;
106
+ ushort nLen = (ushort)nameBytes.Length;
107
+ nameArenaList.AddRange(nameBytes);
108
+
109
+ uint nlOff = (uint)nameLowerList.Count;
110
+ ushort nlLen = (ushort)lowerBytes.Length;
111
+ nameLowerList.AddRange(lowerBytes);
112
+
113
+ Entries.Add(new IndexEntry
114
+ {
115
+ FileRef = r.FileRef,
116
+ ParentRef = r.ParentRef,
117
+ NameOff = nOff,
118
+ NameLowerOff = nlOff,
119
+ NameLen = nLen,
120
+ NameLowerLen = nlLen,
121
+ Flags = r.IsDir ? (byte)1 : (byte)0
122
+ });
123
+ }
124
+
125
+ NameArena = nameArenaList.ToArray();
126
+ NameLowerArena = nameLowerList.ToArray();
127
+ }
128
+
129
+ public void Finalize()
130
+ {
131
+ Entries.Sort((a, b) =>
132
+ {
133
+ var sa = NameLower(a);
134
+ var sb = NameLower(b);
135
+ return string.CompareOrdinal(sa, sb);
136
+ });
137
+ RebuildRefLookup();
138
+ }
139
+
140
+ public CacheData ToCache()
141
+ {
142
+ return new CacheData
143
+ {
144
+ Entries = Entries.Select(e => new CachedEntry
145
+ {
146
+ FileRef = e.FileRef,
147
+ ParentRef = e.ParentRef,
148
+ Name = Name(e),
149
+ Kind = e.Kind
150
+ }).ToList(),
151
+ DriveRoot = DriveRoot,
152
+ Checkpoints = new List<JournalCheckpoint>(Checkpoints)
153
+ };
154
+ }
155
+
156
+ public static IndexStore FromCache(CacheData cache)
157
+ {
158
+ int count = cache.Entries.Count;
159
+ var store = new IndexStore
160
+ {
161
+ DriveRoot = cache.DriveRoot,
162
+ Checkpoints = new List<JournalCheckpoint>(cache.Checkpoints)
163
+ };
164
+ store.Entries.Capacity = count;
165
+
166
+ var nameArenaList = new List<byte>(count * 30);
167
+ var nameLowerList = new List<byte>(count * 30);
168
+
169
+ foreach (var c in cache.Entries)
170
+ {
171
+ string nameLower = c.Name.ToLowerInvariant();
172
+
173
+ byte[] nameBytes = Encoding.UTF8.GetBytes(c.Name);
174
+ byte[] lowerBytes = Encoding.UTF8.GetBytes(nameLower);
175
+
176
+ uint nOff = (uint)nameArenaList.Count;
177
+ ushort nLen = (ushort)nameBytes.Length;
178
+ nameArenaList.AddRange(nameBytes);
179
+
180
+ uint nlOff = (uint)nameLowerList.Count;
181
+ ushort nlLen = (ushort)lowerBytes.Length;
182
+ nameLowerList.AddRange(lowerBytes);
183
+
184
+ store.Entries.Add(new IndexEntry
185
+ {
186
+ FileRef = c.FileRef,
187
+ ParentRef = c.ParentRef,
188
+ NameOff = nOff,
189
+ NameLowerOff = nlOff,
190
+ NameLen = nLen,
191
+ NameLowerLen = nlLen,
192
+ Flags = c.Kind == FileKind.Directory ? (byte)1 : (byte)0
193
+ });
194
+ }
195
+
196
+ store.NameArena = nameArenaList.ToArray();
197
+ store.NameLowerArena = nameLowerList.ToArray();
198
+ store.RebuildRefLookup();
199
+ return store;
200
+ }
201
+
202
+ public void Insert(FileRecord record)
203
+ {
204
+ string nameLower = record.Name.ToLowerInvariant();
205
+
206
+ var nameBytes = Encoding.UTF8.GetBytes(record.Name);
207
+ var lowerBytes = Encoding.UTF8.GetBytes(nameLower);
208
+
209
+ var nameArenaList = new List<byte>(NameArena.Length + nameBytes.Length + 64);
210
+ nameArenaList.AddRange(NameArena);
211
+ var nameLowerList = new List<byte>(NameLowerArena.Length + lowerBytes.Length + 64);
212
+ nameLowerList.AddRange(NameLowerArena);
213
+
214
+ uint nOff = (uint)nameArenaList.Count;
215
+ ushort nLen = (ushort)nameBytes.Length;
216
+ nameArenaList.AddRange(nameBytes);
217
+
218
+ uint nlOff = (uint)nameLowerList.Count;
219
+ ushort nlLen = (ushort)lowerBytes.Length;
220
+ nameLowerList.AddRange(lowerBytes);
221
+
222
+ var entry = new IndexEntry
223
+ {
224
+ FileRef = record.FileRef,
225
+ ParentRef = record.ParentRef,
226
+ NameOff = nOff,
227
+ NameLowerOff = nlOff,
228
+ NameLen = nLen,
229
+ NameLowerLen = nlLen,
230
+ Flags = record.Kind == FileKind.Directory ? (byte)1 : (byte)0
231
+ };
232
+
233
+ int pos = 0;
234
+ for (; pos < Entries.Count; pos++)
235
+ {
236
+ if (string.CompareOrdinal(nameLower, NameLower(Entries[pos])) < 0)
237
+ break;
238
+ }
239
+ Entries.Insert(pos, entry);
240
+
241
+ NameArena = nameArenaList.ToArray();
242
+ NameLowerArena = nameLowerList.ToArray();
243
+ RebuildRefLookup();
244
+ }
245
+
246
+ public void Remove(ulong fileRef)
247
+ {
248
+ Entries.RemoveAll(e => e.FileRef == fileRef);
249
+ RebuildRefLookup();
250
+ }
251
+
252
+ public void Rename(ulong oldRef, FileRecord newRecord)
253
+ {
254
+ Remove(oldRef);
255
+ Insert(newRecord);
256
+ }
257
+
258
+ public void ApplyMove(ulong fileRef, ulong newParentRef, string name, FileKind kind)
259
+ {
260
+ Remove(fileRef);
261
+ Insert(new FileRecord
262
+ {
263
+ FileRef = fileRef,
264
+ ParentRef = newParentRef,
265
+ Name = name,
266
+ Kind = kind
267
+ });
268
+ }
269
+
270
+ public string BuildPath(ulong fileRef)
271
+ {
272
+ var components = new List<string>(16);
273
+ ulong current = fileRef;
274
+
275
+ for (int i = 0; i < 64; i++)
276
+ {
277
+ var idx = LookupIdx(current);
278
+ if (idx == null) break;
279
+
280
+ var entry = Entries[(int)idx];
281
+ components.Add(Name(entry));
282
+ if (entry.ParentRef == current) break;
283
+ current = entry.ParentRef;
284
+ }
285
+
286
+ components.Reverse();
287
+ var path = new StringBuilder(DriveRoot.Length + components.Sum(c => c.Length + 1));
288
+ path.Append(DriveRoot);
289
+ foreach (var comp in components)
290
+ {
291
+ if (path.Length > 0 && path[path.Length - 1] != '\\' && path[path.Length - 1] != '/')
292
+ path.Append('\\');
293
+ path.Append(comp);
294
+ }
295
+ return path.ToString();
296
+ }
297
+ }