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
170 private static class MemPack extends DfsPackDescription {
171 final byte[][] fileMap = new byte[PackExt.values().length][];
172
173 MemPack(String name, DfsRepositoryDescription repoDesc, PackSource source) {
174 super(repoDesc, name, source);
175 }
176
177 void put(PackExt ext, byte[] data) {
178 fileMap[ext.getPosition()] = data;
179 }
180
181 byte[] get(PackExt ext) {
182 return fileMap[ext.getPosition()];
183 }
184 }
185
186 private abstract static class Out extends DfsOutputStream {
187 private final ByteArrayOutputStream dst = new ByteArrayOutputStream();
188 private byte[] data;
189
190 @Override
191 public void write(byte[] buf, int off, int len) {
192 data = null;
193 dst.write(buf, off, len);
194 }
195
196 @Override
197 public int read(long position, ByteBuffer buf) {
198 byte[] d = getData();
199 int n = Math.min(buf.remaining(), d.length - (int) position);
200 if (n == 0)
201 return -1;
202 buf.put(d, (int) position, n);
203 return n;
204 }
205
206 byte[] getData() {
207 if (data == null)
208 data = dst.toByteArray();
209 return data;
210 }
211
212 @Override
213 public abstract void flush();
214
215 @Override
216 public void close() {
217 flush();
218 }
219 }
220
221 private static class ByteArrayReadableChannel implements ReadableChannel {
222 private final byte[] data;
223 private final int blockSize;
224 private int position;
225 private boolean open = true;
226
227 ByteArrayReadableChannel(byte[] buf, int blockSize) {
228 data = buf;
229 this.blockSize = blockSize;
230 }
231
232 @Override
233 public int read(ByteBuffer dst) {
234 int n = Math.min(dst.remaining(), data.length - position);
235 if (n == 0)
236 return -1;
237 dst.put(data, position, n);
238 position += n;
239 return n;
240 }
241
242 @Override
243 public void close() {
244 open = false;
245 }
246
247 @Override
248 public boolean isOpen() {
249 return open;
250 }
251
252 @Override
253 public long position() {
254 return position;
255 }
256
257 @Override
258 public void position(long newPosition) {
259 position = (int) newPosition;
260 }
261
262 @Override
263 public long size() {
264 return data.length;
265 }
266
267 @Override
268 public int blockSize() {
269 return blockSize;
270 }
271
272 @Override
273 public void setReadAheadBytes(int b) {
274
275 }
276 }
277
278
279 protected class MemRefDatabase extends DfsReftableDatabase {
280 boolean performsAtomicTransactions = true;
281
282
283 protected MemRefDatabase() {
284 super(InMemoryRepository.this);
285 }
286
287 @Override
288 public ReftableConfig getReftableConfig() {
289 ReftableConfig cfg = new ReftableConfig();
290 cfg.setAlignBlocks(false);
291 cfg.setIndexObjects(false);
292 cfg.fromConfig(getRepository().getConfig());
293 return cfg;
294 }
295
296 @Override
297 public boolean performsAtomicTransactions() {
298 return performsAtomicTransactions;
299 }
300 }
301 }