Java tutorial
package org.eclipse.jgit.internal.storage.dfs; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.reftable.ReftableConfig; import org.eclipse.jgit.lib.RefDatabase; /** * Git repository stored entirely in the local process memory. * <p> * This implementation builds on the DFS repository by storing all reference and * object data in the local process. It is not very efficient and exists only * for unit testing and small experiments. * <p> * The repository is thread-safe. Memory used is released only when this object * is garbage collected. Closing the repository has no impact on its memory. */ public class InMemoryRepository extends DfsRepository { /** Builder for in-memory repositories. */ public static class Builder extends DfsRepositoryBuilder<Builder, InMemoryRepository> { @Override public InMemoryRepository build() throws IOException { return new InMemoryRepository(this); } } static final AtomicInteger packId = new AtomicInteger(); private final MemObjDatabase objdb; private final MemRefDatabase refdb; private String gitwebDescription; /** * Initialize a new in-memory repository. * * @param repoDesc * description of the repository. */ public InMemoryRepository(DfsRepositoryDescription repoDesc) { this(new Builder().setRepositoryDescription(repoDesc)); } InMemoryRepository(Builder builder) { super(builder); objdb = new MemObjDatabase(this); refdb = new MemRefDatabase(); } /** {@inheritDoc} */ @Override public MemObjDatabase getObjectDatabase() { return objdb; } /** {@inheritDoc} */ @Override public RefDatabase getRefDatabase() { return refdb; } /** * Enable (or disable) the atomic reference transaction support. * <p> * Useful for testing atomic support enabled or disabled. * * @param atomic * whether to use atomic reference transaction support */ public void setPerformsAtomicTransactions(boolean atomic) { refdb.performsAtomicTransactions = atomic; } /** {@inheritDoc} */ @Override @Nullable public String getGitwebDescription() { return gitwebDescription; } /** {@inheritDoc} */ @Override public void setGitwebDescription(@Nullable String d) { gitwebDescription = d; } /** DfsObjDatabase used by InMemoryRepository. */ public static class MemObjDatabase extends DfsObjDatabase { private List<DfsPackDescription> packs = new ArrayList<>(); private int blockSize; MemObjDatabase(DfsRepository repo) { super(repo, new DfsReaderOptions()); } /** * @param blockSize * force a different block size for testing. */ public void setReadableChannelBlockSizeForTest(int blockSize) { this.blockSize = blockSize; } @Override protected synchronized List<DfsPackDescription> listPacks() { return packs; } @Override protected DfsPackDescription newPack(PackSource source) { int id = packId.incrementAndGet(); return new MemPack("pack-" + id + "-" + source.name(), //$NON-NLS-1$ //$NON-NLS-2$ getRepository().getDescription(), source); } @Override protected synchronized void commitPackImpl(Collection<DfsPackDescription> desc, Collection<DfsPackDescription> replace) { List<DfsPackDescription> n; n = new ArrayList<>(desc.size() + packs.size()); n.addAll(desc); n.addAll(packs); if (replace != null) n.removeAll(replace); packs = n; clearCache(); } @Override protected void rollbackPack(Collection<DfsPackDescription> desc) { // Do nothing. Pack is not recorded until commitPack. } @Override protected ReadableChannel openFile(DfsPackDescription desc, PackExt ext) throws FileNotFoundException, IOException { MemPack memPack = (MemPack) desc; byte[] file = memPack.get(ext); if (file == null) throw new FileNotFoundException(desc.getFileName(ext)); return new ByteArrayReadableChannel(file, blockSize); } @Override protected DfsOutputStream writeFile(DfsPackDescription desc, PackExt ext) throws IOException { MemPack memPack = (MemPack) desc; return new Out() { @Override public void flush() { memPack.put(ext, getData()); } }; } } private static class MemPack extends DfsPackDescription { final byte[][] fileMap = new byte[PackExt.values().length][]; MemPack(String name, DfsRepositoryDescription repoDesc, PackSource source) { super(repoDesc, name, source); } void put(PackExt ext, byte[] data) { fileMap[ext.getPosition()] = data; } byte[] get(PackExt ext) { return fileMap[ext.getPosition()]; } } private abstract static class Out extends DfsOutputStream { private final ByteArrayOutputStream dst = new ByteArrayOutputStream(); private byte[] data; @Override public void write(byte[] buf, int off, int len) { data = null; dst.write(buf, off, len); } @Override public int read(long position, ByteBuffer buf) { byte[] d = getData(); int n = Math.min(buf.remaining(), d.length - (int) position); if (n == 0) return -1; buf.put(d, (int) position, n); return n; } byte[] getData() { if (data == null) data = dst.toByteArray(); return data; } @Override public abstract void flush(); @Override public void close() { flush(); } } private static class ByteArrayReadableChannel implements ReadableChannel { private final byte[] data; private final int blockSize; private int position; private boolean open = true; ByteArrayReadableChannel(byte[] buf, int blockSize) { data = buf; this.blockSize = blockSize; } @Override public int read(ByteBuffer dst) { int n = Math.min(dst.remaining(), data.length - position); if (n == 0) return -1; dst.put(data, position, n); position += n; return n; } @Override public void close() { open = false; } @Override public boolean isOpen() { return open; } @Override public long position() { return position; } @Override public void position(long newPosition) { position = (int) newPosition; } @Override public long size() { return data.length; } @Override public int blockSize() { return blockSize; } @Override public void setReadAheadBytes(int b) { // Unnecessary on a byte array. } } /** DfsRefDatabase used by InMemoryRepository. */ protected class MemRefDatabase extends DfsReftableDatabase { boolean performsAtomicTransactions = true; /** Initialize a new in-memory ref database. */ protected MemRefDatabase() { super(InMemoryRepository.this); } @Override public ReftableConfig getReftableConfig() { ReftableConfig cfg = new ReftableConfig(); cfg.setAlignBlocks(false); cfg.setIndexObjects(false); cfg.fromConfig(getRepository().getConfig()); return cfg; } @Override public boolean performsAtomicTransactions() { return performsAtomicTransactions; } } }