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