1 package org.eclipse.jgit.internal.storage.dfs;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.nio.ByteBuffer;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Objects;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.ConcurrentMap;
15 import java.util.concurrent.atomic.AtomicInteger;
16
17 import org.eclipse.jgit.internal.storage.pack.PackExt;
18 import org.eclipse.jgit.lib.ObjectId;
19 import org.eclipse.jgit.lib.ObjectIdRef;
20 import org.eclipse.jgit.lib.Ref;
21 import org.eclipse.jgit.lib.Ref.Storage;
22 import org.eclipse.jgit.lib.SymbolicRef;
23 import org.eclipse.jgit.revwalk.RevWalk;
24 import org.eclipse.jgit.util.RefList;
25
26
27
28
29
30
31
32
33
34
35
36 public class InMemoryRepository extends DfsRepository {
37
38 public static class Builder
39 extends DfsRepositoryBuilder<Builder, InMemoryRepository> {
40 @Override
41 public InMemoryRepository build() throws IOException {
42 return new InMemoryRepository(this);
43 }
44 }
45
46 private static final AtomicInteger packId = new AtomicInteger();
47
48 private final DfsObjDatabase objdb;
49
50 private final DfsRefDatabase refdb;
51
52
53
54
55
56
57
58
59 public InMemoryRepository(DfsRepositoryDescription repoDesc) {
60 this(new Builder().setRepositoryDescription(repoDesc));
61 }
62
63 private InMemoryRepository(Builder builder) {
64 super(builder);
65 objdb = new MemObjDatabase(this);
66 refdb = new MemRefDatabase();
67 }
68
69 @Override
70 public DfsObjDatabase getObjectDatabase() {
71 return objdb;
72 }
73
74 @Override
75 public DfsRefDatabase getRefDatabase() {
76 return refdb;
77 }
78
79 private class MemObjDatabase extends DfsObjDatabase {
80 private List<DfsPackDescription> packs = new ArrayList<DfsPackDescription>();
81
82 MemObjDatabase(DfsRepository repo) {
83 super(repo, new DfsReaderOptions());
84 }
85
86 @Override
87 protected synchronized List<DfsPackDescription> listPacks() {
88 return packs;
89 }
90
91 @Override
92 protected DfsPackDescription newPack(PackSource source) {
93 int id = packId.incrementAndGet();
94 DfsPackDescription desc = new MemPack(
95 "pack-" + id + "-" + source.name(),
96 getRepository().getDescription());
97 return desc.setPackSource(source);
98 }
99
100 @Override
101 protected synchronized void commitPackImpl(
102 Collection<DfsPackDescription> desc,
103 Collection<DfsPackDescription> replace) {
104 List<DfsPackDescription> n;
105 n = new ArrayList<DfsPackDescription>(desc.size() + packs.size());
106 n.addAll(desc);
107 n.addAll(packs);
108 if (replace != null)
109 n.removeAll(replace);
110 packs = n;
111 }
112
113 @Override
114 protected void rollbackPack(Collection<DfsPackDescription> desc) {
115
116 }
117
118 @Override
119 protected ReadableChannel openFile(DfsPackDescription desc, PackExt ext)
120 throws FileNotFoundException, IOException {
121 MemPack memPack = (MemPack) desc;
122 byte[] file = memPack.fileMap.get(ext);
123 if (file == null)
124 throw new FileNotFoundException(desc.getFileName(ext));
125 return new ByteArrayReadableChannel(file);
126 }
127
128 @Override
129 protected DfsOutputStream writeFile(
130 DfsPackDescription desc, final PackExt ext) throws IOException {
131 final MemPack memPack = (MemPack) desc;
132 return new Out() {
133 @Override
134 public void flush() {
135 memPack.fileMap.put(ext, getData());
136 }
137 };
138 }
139 }
140
141 private static class MemPack extends DfsPackDescription {
142 private final Map<PackExt, byte[]>
143 fileMap = new HashMap<PackExt, byte[]>();
144
145 MemPack(String name, DfsRepositoryDescription repoDesc) {
146 super(repoDesc, name);
147 }
148 }
149
150 private abstract static class Out extends DfsOutputStream {
151 private final ByteArrayOutputStream dst = new ByteArrayOutputStream();
152
153 private byte[] data;
154
155 @Override
156 public void write(byte[] buf, int off, int len) {
157 data = null;
158 dst.write(buf, off, len);
159 }
160
161 @Override
162 public int read(long position, ByteBuffer buf) {
163 byte[] d = getData();
164 int n = Math.min(buf.remaining(), d.length - (int) position);
165 if (n == 0)
166 return -1;
167 buf.put(d, (int) position, n);
168 return n;
169 }
170
171 byte[] getData() {
172 if (data == null)
173 data = dst.toByteArray();
174 return data;
175 }
176
177 @Override
178 public abstract void flush();
179
180 @Override
181 public void close() {
182 flush();
183 }
184
185 }
186
187 private static class ByteArrayReadableChannel implements ReadableChannel {
188 private final byte[] data;
189
190 private int position;
191
192 private boolean open = true;
193
194 ByteArrayReadableChannel(byte[] buf) {
195 data = buf;
196 }
197
198 public int read(ByteBuffer dst) {
199 int n = Math.min(dst.remaining(), data.length - position);
200 if (n == 0)
201 return -1;
202 dst.put(data, position, n);
203 position += n;
204 return n;
205 }
206
207 public void close() {
208 open = false;
209 }
210
211 public boolean isOpen() {
212 return open;
213 }
214
215 public long position() {
216 return position;
217 }
218
219 public void position(long newPosition) {
220 position = (int) newPosition;
221 }
222
223 public long size() {
224 return data.length;
225 }
226
227 public int blockSize() {
228 return 0;
229 }
230
231 public void setReadAheadBytes(int b) {
232
233 }
234 }
235
236 private class MemRefDatabase extends DfsRefDatabase {
237 private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<String, Ref>();
238
239 MemRefDatabase() {
240 super(InMemoryRepository.this);
241 }
242
243 @Override
244 protected RefCache scanAllRefs() throws IOException {
245 RefList.Builder<Ref> ids = new RefList.Builder<Ref>();
246 RefList.Builder<Ref> sym = new RefList.Builder<Ref>();
247 for (Ref ref : refs.values()) {
248 if (ref.isSymbolic())
249 sym.add(ref);
250 ids.add(ref);
251 }
252 ids.sort();
253 sym.sort();
254 return new RefCache(ids.toRefList(), sym.toRefList());
255 }
256
257 @Override
258 protected boolean compareAndPut(Ref oldRef, Ref newRef)
259 throws IOException {
260 ObjectId id = newRef.getObjectId();
261 if (id != null) {
262 try (RevWalk rw = new RevWalk(getRepository())) {
263
264
265 rw.parseAny(id);
266 }
267 }
268 String name = newRef.getName();
269 if (oldRef == null)
270 return refs.putIfAbsent(name, newRef) == null;
271
272 synchronized (refs) {
273 Ref cur = refs.get(name);
274 Ref toCompare = cur;
275 if (toCompare != null) {
276 if (toCompare.isSymbolic()) {
277
278
279 Ref leaf = toCompare.getLeaf();
280 if (leaf.getObjectId() == null) {
281 leaf = refs.get(leaf.getName());
282 if (leaf.isSymbolic())
283
284 throw new IllegalArgumentException();
285 toCompare = new SymbolicRef(
286 name,
287 new ObjectIdRef.Unpeeled(
288 Storage.NEW,
289 leaf.getName(),
290 leaf.getObjectId()));
291 } else
292 toCompare = toCompare.getLeaf();
293 }
294 if (eq(toCompare, oldRef))
295 return refs.replace(name, cur, newRef);
296 }
297 }
298
299 if (oldRef.getStorage() == Storage.NEW)
300 return refs.putIfAbsent(name, newRef) == null;
301
302 return false;
303 }
304
305 @Override
306 protected boolean compareAndRemove(Ref oldRef) throws IOException {
307 String name = oldRef.getName();
308 Ref cur = refs.get(name);
309 if (cur != null && eq(cur, oldRef))
310 return refs.remove(name, cur);
311 else
312 return false;
313 }
314
315 private boolean eq(Ref a, Ref b) {
316 if (!Objects.equals(a.getName(), b.getName()))
317 return false;
318
319
320 return Objects.equals(a.getLeaf().getObjectId(),
321 b.getLeaf().getObjectId());
322 }
323 }
324 }