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