View Javadoc
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   * Git repository stored entirely in the local process memory.
19   * <p>
20   * This implementation builds on the DFS repository by storing all reference and
21   * object data in the local process. It is not very efficient and exists only
22   * for unit testing and small experiments.
23   * <p>
24   * The repository is thread-safe. Memory used is released only when this object
25   * is garbage collected. Closing the repository has no impact on its memory.
26   */
27  public class InMemoryRepository extends DfsRepository {
28  	/** Builder for in-memory repositories. */
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  	 * Initialize a new in-memory repository.
45  	 *
46  	 * @param repoDesc
47  	 *            description of the repository.
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  	/** {@inheritDoc} */
60  	@Override
61  	public MemObjDatabase getObjectDatabase() {
62  		return objdb;
63  	}
64  
65  	/** {@inheritDoc} */
66  	@Override
67  	public RefDatabase getRefDatabase() {
68  		return refdb;
69  	}
70  
71  	/**
72  	 * Enable (or disable) the atomic reference transaction support.
73  	 * <p>
74  	 * Useful for testing atomic support enabled or disabled.
75  	 *
76  	 * @param atomic
77  	 *            whether to use atomic reference transaction support
78  	 */
79  	public void setPerformsAtomicTransactions(boolean atomic) {
80  		refdb.performsAtomicTransactions = atomic;
81  	}
82  
83  	/** {@inheritDoc} */
84  	@Override
85  	@Nullable
86  	public String getGitwebDescription() {
87  		return gitwebDescription;
88  	}
89  
90  	/** {@inheritDoc} */
91  	@Override
92  	public void setGitwebDescription(@Nullable String d) {
93  		gitwebDescription = d;
94  	}
95  
96  	/** DfsObjDatabase used by InMemoryRepository. */
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 		 * @param blockSize
107 		 *            force a different block size for testing.
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(), //$NON-NLS-1$ //$NON-NLS-2$
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 			// Do nothing. Pack is not recorded until commitPack.
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 			// Unnecessary on a byte array.
274 		}
275 	}
276 
277 	/** DfsRefDatabase used by InMemoryRepository. */
278 	protected class MemRefDatabase extends DfsReftableDatabase {
279 		boolean performsAtomicTransactions = true;
280 
281 		/** Initialize a new in-memory ref database. */
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 }