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.
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.
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.
/**
* 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. /** 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.
Params: - repoDesc –
description of the repository.
/**
* 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} /** {@inheritDoc} */
@Override
public MemObjDatabase getObjectDatabase() {
return objdb;
}
{@inheritDoc} /** {@inheritDoc} */
@Override
public RefDatabase getRefDatabase() {
return refdb;
}
Enable (or disable) the atomic reference transaction support.
Useful for testing atomic support enabled or disabled.
Params: - atomic –
whether to use atomic reference transaction support
/**
* 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} /** {@inheritDoc} */
@Override
@Nullable
public String getGitwebDescription() {
return gitwebDescription;
}
{@inheritDoc} /** {@inheritDoc} */
@Override
public void setGitwebDescription(@Nullable String d) {
gitwebDescription = d;
}
DfsObjDatabase used by InMemoryRepository. /** 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());
}
Params: - blockSize –
force a different block size for testing.
/**
* @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. /** DfsRefDatabase used by InMemoryRepository. */
protected class MemRefDatabase extends DfsReftableDatabase {
boolean performsAtomicTransactions = true;
Initialize a new in-memory ref database. /** 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;
}
}
}