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 package org.eclipse.jgit.internal.storage.file;
46
47 import java.io.File;
48 import java.io.IOException;
49 import java.util.Collection;
50 import java.util.HashSet;
51 import java.util.Set;
52
53 import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
54 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
55 import org.eclipse.jgit.internal.storage.pack.PackWriter;
56 import org.eclipse.jgit.lib.AbbreviatedObjectId;
57 import org.eclipse.jgit.lib.AnyObjectId;
58 import org.eclipse.jgit.lib.Config;
59 import org.eclipse.jgit.lib.Constants;
60 import org.eclipse.jgit.lib.ObjectDatabase;
61 import org.eclipse.jgit.lib.ObjectId;
62 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
63 import org.eclipse.jgit.lib.ObjectLoader;
64 import org.eclipse.jgit.util.FS;
65
66
67
68
69
70
71
72 class CachedObjectDirectory extends FileObjectDatabase {
73
74
75
76
77 private ObjectIdOwnerMap<UnpackedObjectId> unpackedObjects;
78
79 private final ObjectDirectory wrapped;
80
81 private CachedObjectDirectory[] alts;
82
83
84
85
86
87
88
89 CachedObjectDirectory(ObjectDirectory wrapped) {
90 this.wrapped = wrapped;
91 this.unpackedObjects = scanLoose();
92 }
93
94 private ObjectIdOwnerMap<UnpackedObjectId> scanLoose() {
95 ObjectIdOwnerMap<UnpackedObjectId> m = new ObjectIdOwnerMap<>();
96 File objects = wrapped.getDirectory();
97 String[] fanout = objects.list();
98 if (fanout == null)
99 return m;
100 for (String d : fanout) {
101 if (d.length() != 2)
102 continue;
103 String[] entries = new File(objects, d).list();
104 if (entries == null)
105 continue;
106 for (String e : entries) {
107 if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
108 continue;
109 try {
110 ObjectId id = ObjectId.fromString(d + e);
111 m.add(new UnpackedObjectId(id));
112 } catch (IllegalArgumentException notAnObject) {
113
114 }
115 }
116 }
117 return m;
118 }
119
120
121 @Override
122 public void close() {
123
124 }
125
126
127 @Override
128 public ObjectDatabase newCachedDatabase() {
129 return this;
130 }
131
132 @Override
133 File getDirectory() {
134 return wrapped.getDirectory();
135 }
136
137 @Override
138 File fileFor(AnyObjectId id) {
139 return wrapped.fileFor(id);
140 }
141
142 @Override
143 Config getConfig() {
144 return wrapped.getConfig();
145 }
146
147 @Override
148 FS getFS() {
149 return wrapped.getFS();
150 }
151
152 @Override
153 Set<ObjectId> getShallowCommits() throws IOException {
154 return wrapped.getShallowCommits();
155 }
156
157 private CachedObjectDirectory[] myAlternates() {
158 if (alts == null) {
159 ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates();
160 alts = new CachedObjectDirectory[src.length];
161 for (int i = 0; i < alts.length; i++)
162 alts[i] = src[i].db.newCachedFileObjectDatabase();
163 }
164 return alts;
165 }
166
167 private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
168 Set<AlternateHandle.Id> withMe = new HashSet<>();
169 if (skips != null) {
170 withMe.addAll(skips);
171 }
172 withMe.add(getAlternateId());
173 return withMe;
174 }
175
176 @Override
177 void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
178 throws IOException {
179 wrapped.resolve(matches, id);
180 }
181
182
183 @Override
184 public boolean has(AnyObjectId objectId) throws IOException {
185 return has(objectId, null);
186 }
187
188 private boolean has(AnyObjectId objectId, Set<AlternateHandle.Id> skips)
189 throws IOException {
190 if (unpackedObjects.contains(objectId)) {
191 return true;
192 }
193 if (wrapped.hasPackedObject(objectId)) {
194 return true;
195 }
196 skips = skipMe(skips);
197 for (CachedObjectDirectory alt : myAlternates()) {
198 if (!skips.contains(alt.getAlternateId())) {
199 if (alt.has(objectId, skips)) {
200 return true;
201 }
202 }
203 }
204 return false;
205 }
206
207 @Override
208 ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
209 throws IOException {
210 return openObject(curs, objectId, null);
211 }
212
213 private ObjectLoader openObject(final WindowCursor curs,
214 final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
215 throws IOException {
216 ObjectLoader ldr = openLooseObject(curs, objectId);
217 if (ldr != null) {
218 return ldr;
219 }
220 ldr = wrapped.openPackedObject(curs, objectId);
221 if (ldr != null) {
222 return ldr;
223 }
224 skips = skipMe(skips);
225 for (CachedObjectDirectory alt : myAlternates()) {
226 if (!skips.contains(alt.getAlternateId())) {
227 ldr = alt.openObject(curs, objectId, skips);
228 if (ldr != null) {
229 return ldr;
230 }
231 }
232 }
233 return null;
234 }
235
236 @Override
237 long getObjectSize(WindowCursor curs, AnyObjectId objectId)
238 throws IOException {
239
240
241 return wrapped.getObjectSize(curs, objectId);
242 }
243
244 @Override
245 ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
246 throws IOException {
247 if (unpackedObjects.contains(id)) {
248 ObjectLoader ldr = wrapped.openLooseObject(curs, id);
249 if (ldr != null)
250 return ldr;
251 unpackedObjects = scanLoose();
252 }
253 return null;
254 }
255
256 @Override
257 InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId objectId,
258 boolean createDuplicate) throws IOException {
259 InsertLooseObjectResult result = wrapped.insertUnpackedObject(tmp,
260 objectId, createDuplicate);
261 switch (result) {
262 case INSERTED:
263 case EXISTS_LOOSE:
264 unpackedObjects.addIfAbsent(new UnpackedObjectId(objectId));
265 break;
266
267 case EXISTS_PACKED:
268 case FAILURE:
269 break;
270 }
271 return result;
272 }
273
274 @Override
275 PackFile openPack(File pack) throws IOException {
276 return wrapped.openPack(pack);
277 }
278
279 @Override
280 void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
281 WindowCursor curs) throws IOException {
282 wrapped.selectObjectRepresentation(packer, otp, curs);
283 }
284
285 @Override
286 Collection<PackFile> getPacks() {
287 return wrapped.getPacks();
288 }
289
290 private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
291 UnpackedObjectId(AnyObjectId id) {
292 super(id);
293 }
294 }
295
296 private AlternateHandle.Id getAlternateId() {
297 return wrapped.getAlternateId();
298 }
299 }