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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
/*
* Copyright (c) 2024, Google LLC and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.ReadableChannelSupplier;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.Ref;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.RefLoader;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
import org.eclipse.jgit.internal.storage.pack.PackExt;
/**
* A table that holds multiple cache tables accessed by {@link PackExt} types.
*
* <p>
* Allows the separation of entries from different {@link PackExt} types to
* limit churn in cache caused by entries of differing sizes.
* <p>
* Separating these tables enables the fine-tuning of cache tables per extension
* type.
*/
class PackExtBlockCacheTable implements DfsBlockCacheTable {
/**
* Table name.
*/
private final String name;
private final DfsBlockCacheTable defaultBlockCacheTable;
// Holds the unique tables backing the extBlockCacheTables values.
private final List<DfsBlockCacheTable> blockCacheTableList;
// Holds the mapping of PackExt to DfsBlockCacheTables.
// The relation between the size of extBlockCacheTables entries and
// blockCacheTableList entries is:
// blockCacheTableList.size() <= extBlockCacheTables.size()
private final Map<PackExt, DfsBlockCacheTable> extBlockCacheTables;
/**
* Builds the PackExtBlockCacheTable from a list of
* {@link DfsBlockCachePackExtConfig}s.
*
* @param cacheConfig
* {@link DfsBlockCacheConfig} containing
* {@link DfsBlockCachePackExtConfig}s used to configure
* PackExtBlockCacheTable. The {@link DfsBlockCacheConfig} holds
* the configuration for the default cache table.
* @return the cache table built from the given configs.
* @throws IllegalArgumentException
* when no {@link DfsBlockCachePackExtConfig} exists in the
* {@link DfsBlockCacheConfig}.
*/
static PackExtBlockCacheTable fromBlockCacheConfigs(
DfsBlockCacheConfig cacheConfig) {
DfsBlockCacheTable defaultTable = new ClockBlockCacheTable(cacheConfig);
Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables = new HashMap<>();
List<DfsBlockCachePackExtConfig> packExtConfigs = cacheConfig
.getPackExtCacheConfigurations();
if (packExtConfigs == null || packExtConfigs.size() == 0) {
throw new IllegalArgumentException(
JGitText.get().noPackExtConfigurationGiven);
}
for (DfsBlockCachePackExtConfig packExtCacheConfig : packExtConfigs) {
DfsBlockCacheTable table = new ClockBlockCacheTable(
packExtCacheConfig.getPackExtCacheConfiguration());
for (PackExt packExt : packExtCacheConfig.getPackExts()) {
if (packExtBlockCacheTables.containsKey(packExt)) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().duplicatePackExtensionsForCacheTables,
packExt));
}
packExtBlockCacheTables.put(packExt, table);
}
}
return fromCacheTables(defaultTable, packExtBlockCacheTables);
}
/**
* Creates a new PackExtBlockCacheTable from the combination of a default
* {@link DfsBlockCacheTable} and a map of {@link PackExt}s to
* {@link DfsBlockCacheTable}s.
* <p>
* This method allows for the PackExtBlockCacheTable to handle a mapping of
* {@link PackExt}s to arbitrarily defined {@link DfsBlockCacheTable}
* implementations. This is especially useful for users wishing to implement
* custom cache tables.
* <p>
* This is currently made visible for testing.
*
* @param defaultBlockCacheTable
* the default table used when a handling a {@link PackExt} type
* that does not map to a {@link DfsBlockCacheTable} mapped by
* packExtsCacheTablePairs.
* @param packExtBlockCacheTables
* the mapping of {@link PackExt}s to
* {@link DfsBlockCacheTable}s. A single
* {@link DfsBlockCacheTable} can be defined for multiple
* {@link PackExt}s in a many-to-one relationship.
* @return the PackExtBlockCacheTable created from the
* defaultBlockCacheTable and packExtsCacheTablePairs mapping.
* @throws IllegalArgumentException
* when a {@link PackExt} is defined for multiple
* {@link DfsBlockCacheTable}s.
*/
static PackExtBlockCacheTable fromCacheTables(
DfsBlockCacheTable defaultBlockCacheTable,
Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables) {
Set<DfsBlockCacheTable> blockCacheTables = new HashSet<>();
blockCacheTables.add(defaultBlockCacheTable);
blockCacheTables.addAll(packExtBlockCacheTables.values());
String name = defaultBlockCacheTable.getName() + "," //$NON-NLS-1$
+ packExtBlockCacheTables.values().stream()
.map(DfsBlockCacheTable::getName).sorted()
.collect(Collectors.joining(",")); //$NON-NLS-1$
return new PackExtBlockCacheTable(name, defaultBlockCacheTable,
List.copyOf(blockCacheTables), packExtBlockCacheTables);
}
private PackExtBlockCacheTable(String name,
DfsBlockCacheTable defaultBlockCacheTable,
List<DfsBlockCacheTable> blockCacheTableList,
Map<PackExt, DfsBlockCacheTable> extBlockCacheTables) {
this.name = name;
this.defaultBlockCacheTable = defaultBlockCacheTable;
this.blockCacheTableList = blockCacheTableList;
this.extBlockCacheTables = extBlockCacheTables;
}
@Override
public boolean hasBlock0(DfsStreamKey key) {
return getTable(key).hasBlock0(key);
}
@Override
public DfsBlock getOrLoad(BlockBasedFile file, long position,
DfsReader dfsReader, ReadableChannelSupplier fileChannel)
throws IOException {
return getTable(file.ext).getOrLoad(file, position, dfsReader,
fileChannel);
}
@Override
public <T> Ref<T> getOrLoadRef(DfsStreamKey key, long position,
RefLoader<T> loader) throws IOException {
return getTable(key).getOrLoadRef(key, position, loader);
}
@Override
public void put(DfsBlock v) {
getTable(v.stream).put(v);
}
@Override
public <T> Ref<T> put(DfsStreamKey key, long pos, long size, T v) {
return getTable(key).put(key, pos, size, v);
}
@Override
public <T> Ref<T> putRef(DfsStreamKey key, long size, T v) {
return getTable(key).putRef(key, size, v);
}
@Override
public boolean contains(DfsStreamKey key, long position) {
return getTable(key).contains(key, position);
}
@Override
public <T> T get(DfsStreamKey key, long position) {
return getTable(key).get(key, position);
}
@Override
public List<BlockCacheStats> getBlockCacheStats() {
return blockCacheTableList.stream()
.flatMap(cacheTable -> cacheTable.getBlockCacheStats().stream())
.collect(Collectors.toList());
}
@Override
public String getName() {
return name;
}
private DfsBlockCacheTable getTable(PackExt packExt) {
return extBlockCacheTables.getOrDefault(packExt,
defaultBlockCacheTable);
}
private DfsBlockCacheTable getTable(DfsStreamKey key) {
return extBlockCacheTables.getOrDefault(getPackExt(key),
defaultBlockCacheTable);
}
private static PackExt getPackExt(DfsStreamKey key) {
return PackExt.values()[key.packExtPos];
}
}
|