-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Expand file tree
/
Copy pathlazy-source.ts
More file actions
59 lines (58 loc) · 2.1 KB
/
Copy pathlazy-source.ts
File metadata and controls
59 lines (58 loc) · 2.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import { LRUCache } from 'lru-cache'
import type { Entry, GitReader } from '#design-diff/git'
import { counted } from '#design-diff/metrics'
/** Path enumeration is cheap; blob contents are fetched only by consumers that need them. */
export class LazySources implements Iterable<[string, string]> {
private readonly cached = new LRUCache<string, string>({
maxSize: 96 * 1024 * 1024,
sizeCalculation: (value) => Math.max(1, Buffer.byteLength(value)),
})
private readonly entries: Map<string, Entry>
private readonly order: string[]
private readonly positions: Map<string, number>
constructor(
private readonly reader: GitReader,
entries: Entry[],
private readonly observe: (file: string) => void
) {
this.entries = new Map(entries.map((entry) => [entry.path, entry]))
this.order = [...this.entries.keys()]
this.positions = new Map(this.order.map((file, index) => [file, index]))
}
has(file: string): boolean {
this.observe(file)
return this.entries.has(file)
}
keys(): MapIterator<string> {
return this.entries.keys()
}
get(file: string): string | undefined {
this.observe(file)
const entry = this.entries.get(file)
if (!entry) return undefined
const cached = this.cached.get(file)
if (cached !== undefined) return cached
const start = this.positions.get(file)!
const batch: string[] = []
let bytes = 0
for (const candidate of this.order.slice(start, start + 128)) {
bytes += this.entries.get(candidate)!.size
if (bytes > 8 * 1024 * 1024 && batch.length) break
batch.push(candidate)
}
this.prefetch(batch)
return this.cached.get(file)
}
prefetch(files: string[]): void {
const entries = files
.filter((file) => !this.cached.has(file))
.flatMap((file) => this.entries.get(file) ?? [])
for (const [file, value] of this.reader.blobs(entries)) {
counted('source.loadedBytes', Buffer.byteLength(value))
this.cached.set(file, value)
}
}
*[Symbol.iterator](): IterableIterator<[string, string]> {
for (const file of this.entries.keys()) yield [file, this.get(file)!]
}
}