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