InMemoryRepository.java

  1. package org.eclipse.jgit.internal.storage.dfs;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. import java.nio.ByteBuffer;
  6. import java.util.ArrayList;
  7. import java.util.Collection;
  8. import java.util.List;
  9. import java.util.concurrent.atomic.AtomicInteger;

  10. import org.eclipse.jgit.annotations.Nullable;
  11. import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
  12. import org.eclipse.jgit.internal.storage.pack.PackExt;
  13. import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
  14. import org.eclipse.jgit.lib.RefDatabase;

  15. /**
  16.  * Git repository stored entirely in the local process memory.
  17.  * <p>
  18.  * This implementation builds on the DFS repository by storing all reference and
  19.  * object data in the local process. It is not very efficient and exists only
  20.  * for unit testing and small experiments.
  21.  * <p>
  22.  * The repository is thread-safe. Memory used is released only when this object
  23.  * is garbage collected. Closing the repository has no impact on its memory.
  24.  */
  25. public class InMemoryRepository extends DfsRepository {
  26.     /** Builder for in-memory repositories. */
  27.     public static class Builder
  28.             extends DfsRepositoryBuilder<Builder, InMemoryRepository> {
  29.         @Override
  30.         public InMemoryRepository build() throws IOException {
  31.             return new InMemoryRepository(this);
  32.         }
  33.     }

  34.     static final AtomicInteger packId = new AtomicInteger();

  35.     private final MemObjDatabase objdb;
  36.     private final MemRefDatabase refdb;
  37.     private String gitwebDescription;

  38.     /**
  39.      * Initialize a new in-memory repository.
  40.      *
  41.      * @param repoDesc
  42.      *            description of the repository.
  43.      */
  44.     public InMemoryRepository(DfsRepositoryDescription repoDesc) {
  45.         this(new Builder().setRepositoryDescription(repoDesc));
  46.     }

  47.     InMemoryRepository(Builder builder) {
  48.         super(builder);
  49.         objdb = new MemObjDatabase(this);
  50.         refdb = new MemRefDatabase();
  51.     }

  52.     /** {@inheritDoc} */
  53.     @Override
  54.     public MemObjDatabase getObjectDatabase() {
  55.         return objdb;
  56.     }

  57.     /** {@inheritDoc} */
  58.     @Override
  59.     public RefDatabase getRefDatabase() {
  60.         return refdb;
  61.     }

  62.     /**
  63.      * Enable (or disable) the atomic reference transaction support.
  64.      * <p>
  65.      * Useful for testing atomic support enabled or disabled.
  66.      *
  67.      * @param atomic
  68.      *            whether to use atomic reference transaction support
  69.      */
  70.     public void setPerformsAtomicTransactions(boolean atomic) {
  71.         refdb.performsAtomicTransactions = atomic;
  72.     }

  73.     /** {@inheritDoc} */
  74.     @Override
  75.     @Nullable
  76.     public String getGitwebDescription() {
  77.         return gitwebDescription;
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public void setGitwebDescription(@Nullable String d) {
  82.         gitwebDescription = d;
  83.     }

  84.     /** DfsObjDatabase used by InMemoryRepository. */
  85.     public static class MemObjDatabase extends DfsObjDatabase {
  86.         private List<DfsPackDescription> packs = new ArrayList<>();
  87.         private int blockSize;

  88.         MemObjDatabase(DfsRepository repo) {
  89.             super(repo, new DfsReaderOptions());
  90.         }

  91.         /**
  92.          * @param blockSize
  93.          *            force a different block size for testing.
  94.          */
  95.         public void setReadableChannelBlockSizeForTest(int blockSize) {
  96.             this.blockSize = blockSize;
  97.         }

  98.         @Override
  99.         protected synchronized List<DfsPackDescription> listPacks() {
  100.             return packs;
  101.         }

  102.         @Override
  103.         protected DfsPackDescription newPack(PackSource source) {
  104.             int id = packId.incrementAndGet();
  105.             return new MemPack(
  106.                     "pack-" + id + "-" + source.name(), //$NON-NLS-1$ //$NON-NLS-2$
  107.                     getRepository().getDescription(),
  108.                     source);
  109.         }

  110.         @Override
  111.         protected synchronized void commitPackImpl(
  112.                 Collection<DfsPackDescription> desc,
  113.                 Collection<DfsPackDescription> replace) {
  114.             List<DfsPackDescription> n;
  115.             n = new ArrayList<>(desc.size() + packs.size());
  116.             n.addAll(desc);
  117.             n.addAll(packs);
  118.             if (replace != null)
  119.                 n.removeAll(replace);
  120.             packs = n;
  121.             clearCache();
  122.         }

  123.         @Override
  124.         protected void rollbackPack(Collection<DfsPackDescription> desc) {
  125.             // Do nothing. Pack is not recorded until commitPack.
  126.         }

  127.         @Override
  128.         protected ReadableChannel openFile(DfsPackDescription desc, PackExt ext)
  129.                 throws FileNotFoundException, IOException {
  130.             MemPack memPack = (MemPack) desc;
  131.             byte[] file = memPack.get(ext);
  132.             if (file == null)
  133.                 throw new FileNotFoundException(desc.getFileName(ext));
  134.             return new ByteArrayReadableChannel(file, blockSize);
  135.         }

  136.         @Override
  137.         protected DfsOutputStream writeFile(DfsPackDescription desc,
  138.                 PackExt ext) throws IOException {
  139.             MemPack memPack = (MemPack) desc;
  140.             return new Out() {
  141.                 @Override
  142.                 public void flush() {
  143.                     memPack.put(ext, getData());
  144.                 }
  145.             };
  146.         }
  147.     }

  148.     private static class MemPack extends DfsPackDescription {
  149.         final byte[][] fileMap = new byte[PackExt.values().length][];

  150.         MemPack(String name, DfsRepositoryDescription repoDesc, PackSource source) {
  151.             super(repoDesc, name, source);
  152.         }

  153.         void put(PackExt ext, byte[] data) {
  154.             fileMap[ext.getPosition()] = data;
  155.         }

  156.         byte[] get(PackExt ext) {
  157.             return fileMap[ext.getPosition()];
  158.         }
  159.     }

  160.     private abstract static class Out extends DfsOutputStream {
  161.         private final ByteArrayOutputStream dst = new ByteArrayOutputStream();
  162.         private byte[] data;

  163.         @Override
  164.         public void write(byte[] buf, int off, int len) {
  165.             data = null;
  166.             dst.write(buf, off, len);
  167.         }

  168.         @Override
  169.         public int read(long position, ByteBuffer buf) {
  170.             byte[] d = getData();
  171.             int n = Math.min(buf.remaining(), d.length - (int) position);
  172.             if (n == 0)
  173.                 return -1;
  174.             buf.put(d, (int) position, n);
  175.             return n;
  176.         }

  177.         byte[] getData() {
  178.             if (data == null)
  179.                 data = dst.toByteArray();
  180.             return data;
  181.         }

  182.         @Override
  183.         public abstract void flush();

  184.         @Override
  185.         public void close() {
  186.             flush();
  187.         }
  188.     }

  189.     private static class ByteArrayReadableChannel implements ReadableChannel {
  190.         private final byte[] data;
  191.         private final int blockSize;
  192.         private int position;
  193.         private boolean open = true;

  194.         ByteArrayReadableChannel(byte[] buf, int blockSize) {
  195.             data = buf;
  196.             this.blockSize = blockSize;
  197.         }

  198.         @Override
  199.         public int read(ByteBuffer dst) {
  200.             int n = Math.min(dst.remaining(), data.length - position);
  201.             if (n == 0)
  202.                 return -1;
  203.             dst.put(data, position, n);
  204.             position += n;
  205.             return n;
  206.         }

  207.         @Override
  208.         public void close() {
  209.             open = false;
  210.         }

  211.         @Override
  212.         public boolean isOpen() {
  213.             return open;
  214.         }

  215.         @Override
  216.         public long position() {
  217.             return position;
  218.         }

  219.         @Override
  220.         public void position(long newPosition) {
  221.             position = (int) newPosition;
  222.         }

  223.         @Override
  224.         public long size() {
  225.             return data.length;
  226.         }

  227.         @Override
  228.         public int blockSize() {
  229.             return blockSize;
  230.         }

  231.         @Override
  232.         public void setReadAheadBytes(int b) {
  233.             // Unnecessary on a byte array.
  234.         }
  235.     }

  236.     /** DfsRefDatabase used by InMemoryRepository. */
  237.     protected class MemRefDatabase extends DfsReftableDatabase {
  238.         boolean performsAtomicTransactions = true;

  239.         /** Initialize a new in-memory ref database. */
  240.         protected MemRefDatabase() {
  241.             super(InMemoryRepository.this);
  242.         }

  243.         @Override
  244.         public ReftableConfig getReftableConfig() {
  245.             ReftableConfig cfg = new ReftableConfig();
  246.             cfg.setAlignBlocks(false);
  247.             cfg.setIndexObjects(false);
  248.             cfg.fromConfig(getRepository().getConfig());
  249.             return cfg;
  250.         }

  251.         @Override
  252.         public boolean performsAtomicTransactions() {
  253.             return performsAtomicTransactions;
  254.         }
  255.     }
  256. }