DO NOT SUBMIT mh hack

Change-Id: Iade6952edd09be482ae10f956ee7a611032374b4
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/VerifyReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/VerifyReftable.java
index 2393340..1029668 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/VerifyReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/VerifyReftable.java
@@ -84,7 +84,7 @@
 				ReftableReader reader = new ReftableReader(src)) {
 			scan(refs, reader);
 			seek(refs, reader);
-			byId(refs, reader);
+			// byId(refs, reader);
 		}
 	}
 
@@ -125,7 +125,7 @@
 	}
 
 	@SuppressWarnings("nls")
-	private void byId(List<Ref> refs, ReftableReader reader)
+	void byId(List<Ref> refs, ReftableReader reader)
 			throws IOException {
 		Map<ObjectId, List<Ref>> want = groupById(refs);
 		List<List<Ref>> rnd = new ArrayList<>(want.values());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index c8ea9e2..36a5f31 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -44,19 +44,15 @@
 package org.eclipse.jgit.internal.storage.reftable;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.eclipse.jgit.internal.storage.reftable.BlockWriter.compare;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_BLOCK_TYPE;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
+import static org.eclipse.jgit.internal.storage.reftable.ChunkWriter.compare;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.OBJ_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_1ID;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_2ID;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_NONE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_TEXT;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_TYPE_MASK;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.reverseUpdateIndex;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
@@ -65,27 +61,19 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.io.BlockSource;
-import org.eclipse.jgit.lib.CheckoutEntry;
-import org.eclipse.jgit.lib.InflaterCache;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.util.IntList;
-import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.RawParseUtils;
 
 /** Reads a single block for {@link ReftableReader}. */
 class BlockReader {
 	private byte blockType;
-	private long endPosition;
+	private long addr;
 
 	private byte[] buf;
 	private int bufLen;
@@ -93,19 +81,16 @@
 
 	private int keysStart;
 	private int keysEnd;
-	private int restartIdx;
-	private int restartCount;
 
 	private byte[] nameBuf = new byte[256];
 	private int nameLen;
-	private int valueType;
 
 	byte type() {
 		return blockType;
 	}
 
 	long endPosition() {
-		return endPosition;
+		return addr + bufLen;
 	}
 
 	boolean next() {
@@ -113,9 +98,8 @@
 	}
 
 	void parseKey() {
-		int pfx = readVarint32();
-		valueType = readVarint32();
-		int sfx = valueType >>> 3;
+		int pfx = ptr == keysStart ? 0 : readVarint32();
+		int sfx = readVarint32();
 		if (pfx + sfx > nameBuf.length) {
 			int n = Math.max(pfx + sfx, nameBuf.length * 2);
 			nameBuf = Arrays.copyOf(nameBuf, n);
@@ -147,24 +131,21 @@
 		return compare(match, 0, match.length, nameBuf, 0, len) == 0;
 	}
 
-	boolean matchAbbrevId(byte[] match) {
-		int n = Math.min(match.length, nameLen);
-		return compare(match, 0, n, nameBuf, 0, n) == 0;
-	}
-
 	long readIndex() throws IOException {
 		if (blockType != INDEX_BLOCK_TYPE) {
 			throw invalidBlock();
 		}
-
-		readVarint32(); // skip prefix length
-		int n = readVarint32() >>> 3;
-		ptr += n; // skip name
-		return readVarint64();
+		if (ptr > keysStart) {
+			readVarint32();
+		}
+		int sfx = readVarint32();
+		ptr += sfx;
+		return addr - readVarint64();
 	}
 
 	Ref readRef() throws IOException {
 		String name = RawParseUtils.decode(UTF_8, nameBuf, 0, nameLen);
+		int valueType = buf[ptr++];
 		switch (valueType & VALUE_TYPE_MASK) {
 		case VALUE_NONE: // delete
 			return newRef(name);
@@ -197,61 +178,6 @@
 		}
 	}
 
-	IntList readBlockList() {
-		int n = valueType & VALUE_TYPE_MASK;
-		if (n == 0) {
-			n = readVarint32();
-		}
-		IntList b = new IntList(n);
-		b.add(readVarint32());
-		for (int j = 1; j < n; j++) {
-			int prior = b.get(j - 1);
-			b.add(prior + readVarint32());
-		}
-		return b;
-	}
-
-	long readLogUpdateIndex() {
-		return reverseUpdateIndex(NB.decodeUInt64(nameBuf, nameLen - 8));
-	}
-
-	ReflogEntry readLog() {
-		ObjectId oldId = readValueId();
-		ObjectId newId = readValueId();
-		long ms = readVarint64() * 1000L;
-		short tz = readInt16();
-		String name = readValueString();
-		String email = readValueString();
-		String comment = readValueString();
-
-		return new ReflogEntry() {
-			@Override
-			public ObjectId getOldId() {
-				return oldId;
-			}
-
-			@Override
-			public ObjectId getNewId() {
-				return newId;
-			}
-
-			@Override
-			public PersonIdent getWho() {
-				return new PersonIdent(name, email, ms, tz);
-			}
-
-			@Override
-			public String getComment() {
-				return comment;
-			}
-
-			@Override
-			public CheckoutEntry parseCheckout() {
-				return null;
-			}
-		};
-	}
-
 	private ObjectId readValueId() {
 		ObjectId id = ObjectId.fromRaw(buf, ptr);
 		ptr += OBJECT_ID_LENGTH;
@@ -286,109 +212,21 @@
 			b.flip();
 			b.get(buf);
 		}
-		endPosition = pos + bufLen;
 	}
 
 	private void parseBlockStart(long pos) throws IOException {
+		addr = pos;
 		ptr = 0;
-		if (pos == 0) {
-			if (bufLen == FILE_HEADER_LEN) {
-				setupEmptyFileBlock();
-				return;
-			}
-			ptr += FILE_HEADER_LEN; // first block begins with file header
-		}
-
-		int typeAndSize = NB.decodeInt32(buf, ptr);
-		ptr += 4;
-
-		blockType = (byte) (typeAndSize >>> 24);
-		int blockLen;
-		if ((blockType & INDEX_BLOCK_TYPE) == INDEX_BLOCK_TYPE) {
-			// Index blocks are allowed to grow up to 31-bit blockSize.
-			blockType = INDEX_BLOCK_TYPE;
-			blockLen = decodeIndexSize(typeAndSize);
-		} else {
-			blockLen = typeAndSize & 0xffffff;
-		}
-		if (blockType == LOG_BLOCK_TYPE) {
-			// Log blocks must be inflated after the header.
-			long deflatedSize = inflateBuf(blockLen);
-			endPosition = pos + 4 + deflatedSize;
-		}
-		if (bufLen < blockLen) {
+		blockType = buf[ptr++];
+		int n = readVarint32();
+		if (ptr + n > bufLen || ptr + n > buf.length) {
 			throw invalidBlock();
-		} else if (bufLen > blockLen) {
-			bufLen = blockLen;
 		}
-
+		bufLen = ptr + n;
 		keysStart = ptr;
-		if (blockType != FILE_BLOCK_TYPE) {
-			restartCount = NB.decodeUInt16(buf, bufLen - 2);
-			restartIdx = bufLen - (restartCount * 3 + 2);
-			keysEnd = restartIdx;
-		} else {
-			keysEnd = keysStart;
-		}
-	}
-
-	static int decodeIndexSize(int typeAndSize) {
-		return typeAndSize & 0x7fffffff;
-	}
-
-	private long inflateBuf(int blockLen) throws IOException {
-		byte[] dst = new byte[4 + blockLen];
-		System.arraycopy(buf, 0, dst, 0, 4);
-
-		long deflatedSize = 0;
-		Inflater inf = InflaterCache.get();
-		try {
-			inf.setInput(buf, ptr, bufLen - ptr);
-			for (int o = 4;;) {
-				int n = inf.inflate(dst, o, dst.length - o);
-				o += n;
-				if (inf.finished()) {
-					deflatedSize = inf.getBytesRead();
-					break;
-				} else if (n <= 0) {
-					throw invalidBlock();
-				}
-			}
-		} catch (DataFormatException e) {
-			throw invalidBlock(e);
-		} finally {
-			InflaterCache.release(inf);
-		}
-
-		buf = dst;
-		bufLen = dst.length;
-		return deflatedSize;
-	}
-
-	private void setupEmptyFileBlock() {
-		// An empty reftable has only the file header in first block.
-		blockType = FILE_BLOCK_TYPE;
-		ptr = FILE_HEADER_LEN;
-		restartCount = 0;
-		restartIdx = bufLen;
-		keysStart = bufLen;
 		keysEnd = bufLen;
 	}
 
-	void verifyIndex() throws IOException {
-		if (blockType != INDEX_BLOCK_TYPE) {
-			throw invalidBlock();
-		}
-	}
-
-	int seekKey(byte[] key) {
-		return seek(key, true);
-	}
-
-	int seekAbbrevId(byte[] key) {
-		return seek(key, false);
-	}
-
 	private static int compareKey(boolean useKeyLen, byte[] a,
 			byte[] b, int bi, int bLen) {
 		if (useKeyLen) {
@@ -398,46 +236,13 @@
 		return compare(a, 0, n, b, bi, n);
 	}
 
-	private int seek(byte[] key, boolean useKeyLen) {
-		int low = 0;
-		int end = restartCount;
-		for (;;) {
-			int mid = (low + end) >>> 1;
-			int p = NB.decodeUInt24(buf, restartIdx + mid * 3);
-			ptr = p + 1; // skip 0 prefix length
-			int n = readVarint32() >>> 3;
-			int cmp = compareKey(useKeyLen, key, buf, ptr, n);
-			if (cmp < 0) {
-				end = mid;
-			} else if (cmp == 0) {
-				ptr = p;
-				return 0;
-			} else /* if (cmp > 0) */ {
-				low = mid + 1;
-			}
-			if (low >= end) {
-				return seekToKey(key, useKeyLen, p, low, cmp);
-			}
-		}
-	}
-
-	private int seekToKey(byte[] key, boolean useKeyLen,
-			int rPtr, int rIdx, int rCmp) {
-		if (rCmp < 0) {
-			if (rIdx == 0) {
-				ptr = keysStart;
-				return -1;
-			}
-			ptr = NB.decodeUInt24(buf, restartIdx + (rIdx - 1) * 3);
-		} else {
-			ptr = rPtr;
-		}
-
+	int seekToKey(byte[] key) {
+		ptr = keysStart;
 		int cmp;
 		do {
 			int savePtr = ptr;
 			parseKey();
-			cmp = compareKey(useKeyLen, key, nameBuf, 0, nameLen);
+			cmp = compareKey(true, key, nameBuf, 0, nameLen);
 			if (cmp <= 0) {
 				// cmp < 0, name should be in this block, but is not.
 				// cmp = 0, block is positioned at name.
@@ -452,6 +257,7 @@
 	void skipValue() {
 		switch (blockType) {
 		case REF_BLOCK_TYPE:
+			int valueType = buf[ptr++];
 			switch (valueType & VALUE_TYPE_MASK) {
 			case VALUE_NONE:
 				return;
@@ -467,31 +273,10 @@
 			}
 			break;
 
-		case OBJ_BLOCK_TYPE: {
-			int n = valueType & VALUE_TYPE_MASK;
-			if (n == 0) {
-				n = readVarint32();
-			}
-			while (n-- > 0) {
-				readVarint32();
-			}
-			return;
-		}
-
 		case INDEX_BLOCK_TYPE:
-			readVarint32();
-			return;
-
-		case LOG_BLOCK_TYPE:
-			ptr += 2 * OBJECT_ID_LENGTH; // 2x id;
-			readVarint64(); // time
-			ptr += 2; // 2-byte tz
-			skipString(); // name
-			skipString(); // email
-			skipString(); // comment
+			readVarint64();
 			return;
 		}
-
 		throw new IllegalStateException();
 	}
 
@@ -500,10 +285,6 @@
 		ptr += n;
 	}
 
-	private short readInt16() {
-		return (short) NB.decodeUInt16(buf, ptr += 2);
-	}
-
 	private int readVarint32() {
 		byte c = buf[ptr++];
 		int val = c & 0x7f;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ChunkWriter.java
similarity index 74%
rename from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ChunkWriter.java
index 9c05381..df94e02 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ChunkWriter.java
@@ -46,12 +46,10 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.MAX_RESTARTS;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.OBJ_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_1ID;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_2ID;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_INDEX_RECORD;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_LOG_RECORD;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_NONE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_TEXT;
@@ -73,27 +71,20 @@
 import org.eclipse.jgit.util.IntList;
 import org.eclipse.jgit.util.NB;
 
-class BlockWriter {
-	private final byte blockType;
+class ChunkWriter {
+	private final byte chunkType;
 	private final List<Entry> entries = new ArrayList<>();
 	private final int blockSize;
-	private final int restartInterval;
 
 	private int bytesInKeyTable;
-	private int restartCnt;
 
-	BlockWriter(byte type, int bs, int ri) {
-		blockType = type;
+	ChunkWriter(byte type, int bs) {
+		chunkType = type;
 		blockSize = bs;
-		restartInterval = ri;
 	}
 
 	byte blockType() {
-		return blockType;
-	}
-
-	boolean padBetweenBlocks() {
-		return blockType == REF_BLOCK_TYPE || blockType == OBJ_BLOCK_TYPE;
+		return chunkType;
 	}
 
 	byte[] lastKey() {
@@ -105,120 +96,56 @@
 	}
 
 	int currentSize() {
-		return computeBlockSize(0, false);
-	}
-
-	int estimateIndexSizeIfAdding(byte[] lastKey, long blockOffset) {
-		IndexEntry entry = new IndexEntry(lastKey, blockOffset);
-		return computeBlockSize(entry.size(0), true);
-	}
-
-	void addIndex(byte[] lastKey, long blockOffset) {
-		entries.add(new IndexEntry(lastKey, blockOffset));
+		return computeBlockSize(0);
 	}
 
 	void addFirst(Entry entry) throws BlockSizeTooSmallException {
-		if (!tryAdd(entry, true)) {
+		if (!tryAdd(entry)) {
 			// Insanely long names need a larger block size.
 			throw blockSizeTooSmall(entry);
 		}
 	}
 
 	boolean tryAdd(Entry entry) {
-		if (tryAdd(entry, true)) {
-			return true;
-		} else if (nextShouldBeRestart()) {
-			// It was time for another restart, but the entry doesn't fit
-			// with its complete name, as the block is nearly full. Try to
-			// force it to fit with prefix compression rather than waste
-			// the tail of the block with padding.
-			return tryAdd(entry, false);
-		}
-		return false;
-	}
-
-	private boolean tryAdd(Entry entry, boolean tryRestart) {
 		byte[] key = entry.key;
 		int prefixLen = 0;
-		boolean restart = tryRestart && nextShouldBeRestart();
-		if (!restart) {
+		boolean first = entries.isEmpty();
+		if (!first) {
 			byte[] prior = entries.get(entries.size() - 1).key;
 			prefixLen = commonPrefix(prior, prior.length, key);
-			if (prefixLen == 0) {
-				restart = true;
-			}
 		}
 
-		int entrySize = entry.size(prefixLen);
-		if (computeBlockSize(entrySize, restart) > blockSize) {
+		int entrySize = entry.size(first, prefixLen);
+		if (computeBlockSize(entrySize) > blockSize) {
 			return false;
 		}
 
 		bytesInKeyTable += entrySize;
 		entries.add(entry);
-		if (restart) {
-			entry.restart = true;
-			restartCnt++;
-		}
 		return true;
 	}
 
-	private boolean nextShouldBeRestart() {
-		int cnt = entries.size();
-		return (cnt == 0 || ((cnt + 1) % restartInterval) == 0)
-				&& restartCnt < MAX_RESTARTS;
-	}
-
-	private int computeBlockSize(int key, boolean restart) {
-		return 4 // 4-byte block header
-				+ bytesInKeyTable + key
-				+ (restartCnt + (restart ? 1 : 0)) * 3
-				+ 2; // 2-byte restart_count
+	private int computeBlockSize(int key) {
+		int sz = bytesInKeyTable + key;
+		return 1 + computeVarintSize(sz) + sz;
 	}
 
 	void writeTo(ReftableOutputStream os) throws IOException {
-		if (blockType == INDEX_BLOCK_TYPE) {
-			selectIndexRestarts();
-		}
-		if (restartCnt > MAX_RESTARTS) {
-			throw new IllegalStateException();
-		}
-
-		IntList restartOffsets = new IntList(restartCnt);
-		byte[] prior = {};
-
-		os.beginBlock(blockType);
+		byte[] prior = null;
+		os.beginBlock(chunkType);
+		os.writeVarint(bytesInKeyTable);
 		for (int entryIdx = 0; entryIdx < entries.size(); entryIdx++) {
 			Entry entry = entries.get(entryIdx);
-			if (entry.restart) {
-				restartOffsets.add(os.bytesWrittenInBlock());
-			}
 			entry.writeKey(os, prior);
 			entry.writeValue(os);
 			prior = entry.key;
 		}
-		for (int i = 0; i < restartOffsets.size(); i++) {
-			os.writeInt24(restartOffsets.get(i));
-		}
-		os.writeInt16(restartOffsets.size());
 		os.flushBlock();
 	}
 
-	private void selectIndexRestarts() {
-		// Indexes grow without bound, but the restart table has a limit.
-		// Select restarts in the index as far apart as possible to stay
-		// within the MAX_RESTARTS limit defined by the file format.
-		int ir = Math.max(restartInterval, entries.size() / MAX_RESTARTS);
-		for (int k = 0; k < entries.size(); k++) {
-			if ((k % ir) == 0) {
-				entries.get(k).restart = true;
-			}
-		}
-	}
-
 	private BlockSizeTooSmallException blockSizeTooSmall(Entry entry) {
 		// Compute size required to fit this entry by itself.
-		int min = computeBlockSize(entry.size(0), true);
+		int min = computeBlockSize(entry.size(true, 0));
 		return new BlockSizeTooSmallException(min);
 	}
 
@@ -254,35 +181,32 @@
 		static int compare(Entry ea, Entry eb) {
 			byte[] a = ea.key;
 			byte[] b = eb.key;
-			return BlockWriter.compare(a, 0, a.length, b, 0, b.length);
+			return ChunkWriter.compare(a, 0, a.length, b, 0, b.length);
 		}
 
 		final byte[] key;
-		boolean restart;
 
 		Entry(byte[] key) {
 			this.key = key;
 		}
 
 		void writeKey(ReftableOutputStream os, byte[] prior) {
-			int pfx;
-			int sfx;
-			if (restart) {
-				pfx = 0;
-				sfx = key.length;
+			if (prior == null) {
+				os.writeVarint(key.length);
+				os.write(key, 0, key.length);
 			} else {
-				pfx = commonPrefix(prior, prior.length, key);
-				sfx = key.length - pfx;
+				int pfx = commonPrefix(prior, prior.length, key);
+				int sfx = key.length - pfx;
+				os.writeVarint(pfx);
+				os.writeVarint(sfx);
+				os.write(key, pfx, sfx);
 			}
-			os.writeVarint(pfx);
-			os.writeVarint(encodeSuffixAndType(sfx, valueType()));
-			os.write(key, pfx, sfx);
 		}
 
-		int size(int prefixLen) {
+		int size(boolean first, int prefixLen) {
 			int sfx = key.length - prefixLen;
-			return computeVarintSize(prefixLen)
-					+ computeVarintSize(encodeSuffixAndType(sfx, valueType()))
+			return (first ? 0 : computeVarintSize(prefixLen))
+					+ computeVarintSize(sfx)
 					+ sfx
 					+ valueSize();
 		}
@@ -294,11 +218,11 @@
 	}
 
 	static class IndexEntry extends Entry {
-		private final long blockOffset;
+		private final long childAddr;
 
-		IndexEntry(byte[] key, long blockOffset) {
+		IndexEntry(byte[] key, long childAddr) {
 			super(key);
-			this.blockOffset = blockOffset;
+			this.childAddr = childAddr;
 		}
 
 		@Override
@@ -308,17 +232,24 @@
 
 		@Override
 		int valueType() {
-			return VALUE_INDEX_RECORD;
+			return 0;
 		}
 
 		@Override
 		int valueSize() {
-			return computeVarintSize(blockOffset);
+			return computeVarintSize(childAddr);
 		}
 
 		@Override
 		void writeValue(ReftableOutputStream os) {
-			os.writeVarint(blockOffset);
+			os.writeVarint(childAddr);
+		}
+
+		IndexEntry forIndexAt(long indexAddr) {
+			if (indexAddr <= childAddr) {
+				throw new IllegalArgumentException();
+			}
+			return new IndexEntry(key, indexAddr - childAddr);
 		}
 	}
 
@@ -352,18 +283,20 @@
 		int valueSize() {
 			if (ref.isSymbolic()) {
 				int nameLen = 5 + nameUtf8(ref.getTarget()).length;
-				return computeVarintSize(nameLen) + nameLen;
+				return 1 + computeVarintSize(nameLen) + nameLen;
 			} else if (ref.getStorage() == NEW && ref.getObjectId() == null) {
-				return 0;
+				return 1;
 			} else if (ref.getPeeledObjectId() != null) {
-				return 2 * OBJECT_ID_LENGTH;
+				return 1 + 2 * OBJECT_ID_LENGTH;
 			} else {
-				return OBJECT_ID_LENGTH;
+				return 1 + OBJECT_ID_LENGTH;
 			}
 		}
 
 		@Override
 		void writeValue(ReftableOutputStream os) throws IOException {
+			os.write(valueType());
+
 			if (ref.isSymbolic()) {
 				String target = ref.getTarget().getName();
 				os.writeVarintString("ref: " + target); //$NON-NLS-1$
@@ -412,11 +345,12 @@
 
 		@Override
 		int valueSize() {
-			return computeVarintSize(value.length) + value.length;
+			return 1 + computeVarintSize(value.length) + value.length;
 		}
 
 		@Override
 		void writeValue(ReftableOutputStream os) throws IOException {
+			os.write(valueType());
 			os.writeVarint(value.length);
 			os.write(value);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConfig.java
index ed7d03e..643cc59 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConfig.java
@@ -50,10 +50,10 @@
 
 /** Configuration used by a reftable writer when constructing the stream. */
 public class ReftableConfig {
-	private int refBlockSize = 4 << 10;
+	private int refBlockSize = 1 << 10;
 	private int logBlockSize = 64 << 10;
 	private int restartInterval;
-	private boolean indexObjects = true;
+	private boolean indexObjects = false;
 
 	/** Create a default configuration. */
 	public ReftableConfig() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConstants.java
index 80c3a7d..c0f4f8f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableConstants.java
@@ -47,8 +47,8 @@
 	static final byte[] FILE_HEADER_MAGIC = { 'R', 'E', 'F', 'T' };
 	static final byte VERSION_1 = (byte) 1;
 
-	static final int FILE_HEADER_LEN = 24;
-	static final int FILE_FOOTER_LEN = 68;
+	static final int FILE_HEADER_LEN = 40;
+	static final int FILE_FOOTER_LEN = FILE_HEADER_LEN + 4;
 	static final int MAX_BLOCK_SIZE = (1 << 24) - 1;
 
 	static final byte FILE_BLOCK_TYPE = 'R';
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableOutputStream.java
index 8e5222d..6c8c6f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableOutputStream.java
@@ -44,18 +44,12 @@
 package org.eclipse.jgit.internal.storage.reftable;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Arrays;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
 
-import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.io.CountingOutputStream;
@@ -69,12 +63,8 @@
 class ReftableOutputStream extends OutputStream {
 	private final byte[] tmp = new byte[10];
 	private final CountingOutputStream out;
-	private final Deflater deflater;
-	private final DeflaterOutputStream compressor;
 
-	private int blockType;
 	private int blockSize;
-	private int blockStart;
 	private byte[] blockBuf;
 	private int cur;
 
@@ -86,8 +76,6 @@
 		blockBuf = new byte[bs];
 
 		out = new CountingOutputStream(os);
-		deflater = new Deflater(Deflater.BEST_COMPRESSION);
-		compressor = new DeflaterOutputStream(out, deflater);
 	}
 
 	void setBlockSize(int bs) {
@@ -180,67 +168,25 @@
 	}
 
 	void flushFileHeader() throws IOException {
-		if (cur == FILE_HEADER_LEN && out.getCount() == 0) {
-			// If the file will be a log only file, flush the file header.
-			out.write(blockBuf, 0, cur);
-			cur = 0;
-		}
+		out.write(blockBuf, 0, cur);
+		cur = 0;
 	}
 
 	void beginBlock(byte id) {
-		blockType = id;
-		blockStart = cur;
-		cur += 4; // reserve space for 4-byte block header.
+		blockBuf[cur++] = id;
 	}
 
 	void flushBlock() throws IOException {
-		if (cur > blockSize && !isIndexBlock()) {
-			throw new IOException(JGitText.get().overflowedReftableBlock);
-		}
-		NB.encodeInt32(blockBuf, blockStart, (blockType << 24) | cur);
-
-		if (blockType == LOG_BLOCK_TYPE) {
-			// Log blocks are deflated after the block header.
-			deflater.reset();
-			out.write(blockBuf, 0, 4);
-			compressor.write(blockBuf, 4, cur - 4);
-			compressor.finish();
-		} else {
-			// Other blocks are uncompressed.
-			out.write(blockBuf, 0, cur);
-		}
+		out.write(blockBuf, 0, cur);
 
 		cur = 0;
-		blockType = 0;
-		blockStart = 0;
 		blockCount++;
 	}
 
-	void padBetweenBlocksToNextBlock() throws IOException {
-		long m = size() % blockSize;
-		if (m > 0) {
-			int pad = blockSize - (int) m;
-			ensureBytesAvailableInBlockBuf(pad);
-			Arrays.fill(blockBuf, 0, pad, (byte) 0);
-			out.write(blockBuf, 0, pad);
-			paddingUsed += pad;
-		}
-	}
-
-	int estimatePadBetweenBlocks(int currentBlockSize) {
-		long m = (size() + currentBlockSize) % blockSize;
-		return m > 0 ? blockSize - (int) m : 0;
-	}
-
-	private boolean isIndexBlock() {
-		return (blockType & INDEX_BLOCK_TYPE) == INDEX_BLOCK_TYPE;
-	}
-
 	void finishFile() throws IOException {
 		// File footer doesn't need patching for the block start.
 		// Just flush what has been buffered.
 		out.write(blockBuf, 0, cur);
 		cur = 0;
-		deflater.end();
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
index 3360eb7..b3bf33a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
@@ -44,14 +44,11 @@
 package org.eclipse.jgit.internal.storage.reftable;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.eclipse.jgit.internal.storage.reftable.BlockReader.decodeIndexSize;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_FOOTER_LEN;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VERSION_1;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.isFileHeaderMagic;
-import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -61,12 +58,8 @@
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.io.BlockSource;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.LogEntry;
 import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.ReflogEntry;
-import org.eclipse.jgit.util.IntList;
 import org.eclipse.jgit.util.NB;
 
 /**
@@ -78,23 +71,12 @@
 public class ReftableReader extends Reftable {
 	private final BlockSource src;
 
-	private int blockSize;
 	private long minUpdateIndex;
 	private long maxUpdateIndex;
+	private long refRootChunkAddr;
+	long objRootChunkAddr;
 
-	private long refEnd;
-	private long objOffset;
-	private long objEnd;
-	private long logOffset;
-	private long logEnd;
-
-	private long refIndexOffset = -1;
-	private long objIndexOffset = -1;
-	private long logIndexOffset = -1;
-
-	private BlockReader refIndex;
-	private BlockReader objIndex;
-	private BlockReader logIndex;
+	private BlockReader refRoot;
 
 	/**
 	 * Initialize a new reftable reader.
@@ -114,10 +96,7 @@
 	 *             file cannot be read.
 	 */
 	public int blockSize() throws IOException {
-		if (blockSize == 0) {
-			readFileHeader();
-		}
-		return blockSize;
+		return 0;
 	}
 
 	/**
@@ -128,7 +107,7 @@
 	 *             file cannot be read.
 	 */
 	public long minUpdateIndex() throws IOException {
-		if (blockSize == 0) {
+		if (minUpdateIndex == 0) {
 			readFileHeader();
 		}
 		return minUpdateIndex;
@@ -142,7 +121,7 @@
 	 *             file cannot be read.
 	 */
 	public long maxUpdateIndex() throws IOException {
-		if (blockSize == 0) {
+		if (minUpdateIndex == 0) {
 			readFileHeader();
 		}
 		return maxUpdateIndex;
@@ -150,21 +129,17 @@
 
 	@Override
 	public RefCursor allRefs() throws IOException {
-		if (blockSize == 0) {
-			readFileHeader();
-		}
-
-		long end = refEnd > 0 ? refEnd : (src.size() - FILE_FOOTER_LEN);
-		src.adviseSequentialRead(0, end);
-
+		long end = src.size() - FILE_FOOTER_LEN;
 		RefCursorImpl i = new RefCursorImpl(end, null, false);
-		i.block = readBlock(0, end);
+		i.block = readBlock(FILE_HEADER_LEN, end);
 		return i;
 	}
 
 	@Override
 	public RefCursor seek(String refName) throws IOException {
-		initRefIndex();
+		if (refRootChunkAddr == 0) {
+			readFileFooter();
+		}
 
 		byte[] match = refName.getBytes(UTF_8);
 		boolean prefix = match[match.length - 1] == '/';
@@ -174,87 +149,46 @@
 			key[key.length - 1] = '\1';
 		}
 
-		RefCursorImpl i = new RefCursorImpl(refEnd, match, prefix);
-		i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
+		long end = src.size() - FILE_FOOTER_LEN;
+		RefCursorImpl i = new RefCursorImpl(end, match, prefix);
+		i.block = seek(key, refRootChunkAddr, end);
 		return i;
 	}
 
 	@Override
 	public RefCursor byObjectId(AnyObjectId id) throws IOException {
-		initObjIndex();
-		ObjCursorImpl i = new ObjCursorImpl(refEnd, id);
-		if (objIndex != null) {
-			i.initSeek();
-		} else {
-			i.initScan();
-		}
-		return i;
+		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public LogCursor allLogs() throws IOException {
-		initLogIndex();
-		if (logOffset > 0) {
-			src.adviseSequentialRead(logOffset, logEnd);
-			LogCursorImpl i = new LogCursorImpl(logEnd, null);
-			i.block = readBlock(logOffset, logEnd);
-			return i;
-		}
 		return new EmptyLogCursor();
 	}
 
 	@Override
 	public LogCursor seekLog(String refName, long updateIndex)
 			throws IOException {
-		initLogIndex();
-		if (logOffset > 0) {
-			byte[] key = LogEntry.key(refName, updateIndex);
-			byte[] match = refName.getBytes(UTF_8);
-			LogCursorImpl i = new LogCursorImpl(logEnd, match);
-			i.block = seek(LOG_BLOCK_TYPE, key, logIndex, logOffset, logEnd);
-			return i;
-		}
 		return new EmptyLogCursor();
 	}
 
-	private BlockReader seek(byte blockType, byte[] key, BlockReader idx,
-			long start, long end) throws IOException {
-		if (idx != null) {
-			long blockOffset = idx.seekKey(key) <= 0
-					? idx.readIndex()
-					: ((blocksIn(start, end) - 1) * blockSize);
-			BlockReader block = readBlock(blockOffset, end);
-			block.seekKey(key);
-			return block;
+	private BlockReader seek(byte[] key, long addr, long end)
+			throws IOException {
+		BlockReader block = refRoot;
+		if (block == null) {
+			block = readBlock(addr, end);
+			refRoot = block;
 		}
-		return binarySearch(blockType, key, start, end);
-	}
-
-	private BlockReader binarySearch(byte blockType, byte[] name,
-			long startPos, long endPos) throws IOException {
-		int low = (int) (startPos / blockSize);
-		int end = blocksIn(startPos, endPos);
-		BlockReader block = null;
 		do {
-			int mid = (low + end) >>> 1;
-			block = readBlock(((long) mid) * blockSize, endPos);
-			if (blockType != block.type()) {
-				return null;
-			}
-			int cmp = block.seekKey(name);
-			if (cmp < 0) {
-				end = mid;
-			} else if (cmp == 0) {
-				break;
-			} else /* if (cmp > 0) */ {
-				low = mid + 1;
-			}
-		} while (low < end);
+			block.seekToKey(key);
+			addr = block.readIndex();
+			block = readBlock(addr, end);
+		} while (block.type() == ReftableConstants.INDEX_BLOCK_TYPE);
+		block.seekToKey(key);
 		return block;
 	}
 
 	private void readFileHeader() throws IOException {
-		readHeaderOrFooter(0, FILE_HEADER_LEN);
+		readFileFooter();
 	}
 
 	private void readFileFooter() throws IOException {
@@ -266,40 +200,6 @@
 		if (crc.getValue() != NB.decodeUInt32(ftr, ftrLen - 4)) {
 			throw new IOException(JGitText.get().invalidReftableCRC);
 		}
-
-		refIndexOffset = NB.decodeInt64(ftr, 24);
-		objOffset = NB.decodeInt64(ftr, 32);
-		objIndexOffset = NB.decodeInt64(ftr, 40);
-		logOffset = NB.decodeInt64(ftr, 48);
-		logIndexOffset = NB.decodeInt64(ftr, 56);
-
-		if (refIndexOffset > 0) {
-			refEnd = refIndexOffset;
-		} else if (objOffset > 0) {
-			refEnd = objOffset;
-		} else if (logOffset > 0) {
-			refEnd = logOffset;
-		} else {
-			refEnd = src.size() - ftrLen;
-		}
-
-		if (objOffset > 0) {
-			if (objIndexOffset > 0) {
-				objEnd = objIndexOffset;
-			} else if (logOffset > 0) {
-				objEnd = logOffset;
-			} else {
-				objEnd = src.size() - ftrLen;
-			}
-		}
-
-		if (logOffset > 0) {
-			if (logIndexOffset > 0) {
-				logEnd = logIndexOffset;
-			} else {
-				logEnd = src.size() - ftrLen;
-			}
-		}
 	}
 
 	private byte[] readHeaderOrFooter(long pos, int len) throws IOException {
@@ -322,81 +222,23 @@
 					JGitText.get().unsupportedReftableVersion,
 					Integer.valueOf(version)));
 		}
-		if (blockSize == 0) {
-			blockSize = v & 0xffffff;
-		}
 		minUpdateIndex = NB.decodeInt64(tmp, 8);
 		maxUpdateIndex = NB.decodeInt64(tmp, 16);
+		refRootChunkAddr = NB.decodeInt64(tmp, 24);
+		objRootChunkAddr = NB.decodeInt64(tmp, 32);
 		return tmp;
 	}
 
-	private void initRefIndex() throws IOException {
-		if (blockSize == 0 || refIndexOffset < 0) {
-			readFileFooter();
-		}
-		if (refIndex == null && refIndexOffset > 0) {
-			refIndex = readIndex(refIndexOffset);
-		}
-	}
-
-	private void initObjIndex() throws IOException {
-		if (blockSize == 0 || objIndexOffset < 0) {
-			readFileFooter();
-		}
-		if (objIndex == null && objIndexOffset > 0) {
-			objIndex = readIndex(objIndexOffset);
-		}
-	}
-
-	private void initLogIndex() throws IOException {
-		if (blockSize == 0 || logIndexOffset < 0) {
-			readFileFooter();
-		}
-		if (logIndex == null && logIndexOffset > 0) {
-			logIndex = readIndex(logIndexOffset);
-		}
-	}
-
-	private BlockReader readIndex(long pos) throws IOException {
-		int sz = readIndexSize(pos);
-		BlockReader i = new BlockReader();
-		i.readBlock(src, pos, sz);
-		i.verifyIndex();
-		return i;
-	}
-
-	private int readIndexSize(long pos) throws IOException {
-		ByteBuffer tmp = src.read(pos, 4);
-		if (tmp.position() < 4) {
-			throw new IOException(JGitText.get().invalidReftableFile);
-		}
-		byte[] buf;
-		if (tmp.hasArray() && tmp.arrayOffset() == 0) {
-			buf = tmp.array();
-		} else {
-			buf = new byte[4];
-			tmp.flip();
-			tmp.get(buf, 0, 4);
-		}
-		return decodeIndexSize(NB.decodeInt32(buf, 0));
-	}
-
 	private BlockReader readBlock(long position, long end) throws IOException {
-		int sz = blockSize;
+		int sz = 1024; // what a fucking hack
 		if (position + sz > end) {
-			sz = (int) (end - position); // last block may omit padding.
+			sz = (int) (end - position);
 		}
-
 		BlockReader b = new BlockReader();
 		b.readBlock(src, position, sz);
 		return b;
 	}
 
-	private int blocksIn(long pos, long end) {
-		int blocks = (int) ((end - pos) / blockSize);
-		return end % blockSize == 0 ? blocks : (blocks + 1);
-	}
-
 	/**
 	 * Get size of the reftable, in bytes.
 	 *
@@ -465,154 +307,4 @@
 			// Do nothing.
 		}
 	}
-
-	private class LogCursorImpl extends LogCursor {
-		private final long scanEnd;
-		private final byte[] match;
-
-		private String refName;
-		private long updateIndex;
-		private ReflogEntry entry;
-		BlockReader block;
-
-		LogCursorImpl(long scanEnd, byte[] match) {
-			this.scanEnd = scanEnd;
-			this.match = match;
-		}
-
-		@Override
-		public boolean next() throws IOException {
-			for (;;) {
-				if (block == null || block.type() != LOG_BLOCK_TYPE) {
-					return false;
-				} else if (!block.next()) {
-					long p = block.endPosition();
-					if (p >= scanEnd) {
-						return false;
-					}
-					block = readBlock(p, scanEnd);
-					continue;
-				}
-
-				block.parseKey();
-				if (match != null && !block.match(match, false)) {
-					block.skipValue();
-					return false;
-				}
-
-				refName = block.name();
-				updateIndex = block.readLogUpdateIndex();
-				entry = block.readLog();
-				return true;
-			}
-		}
-
-		@Override
-		public String getRefName() {
-			return refName;
-		}
-
-		@Override
-		public long getUpdateIndex() {
-			return updateIndex;
-		}
-
-		@Override
-		public ReflogEntry getReflogEntry() {
-			return entry;
-		}
-
-		@Override
-		public void close() {
-			// Do nothing.
-		}
-	}
-
-	private class ObjCursorImpl extends RefCursor {
-		private final long scanEnd;
-		private final ObjectId match;
-
-		private Ref ref;
-		private int listIdx;
-		private IntList blockList;
-		private BlockReader block;
-
-		ObjCursorImpl(long scanEnd, AnyObjectId id) {
-			this.scanEnd = scanEnd;
-			this.match = id.copy();
-		}
-
-		void initSeek() throws IOException {
-			byte[] key = new byte[OBJECT_ID_LENGTH];
-			match.copyRawTo(key, 0);
-
-			long blockOffset = objIndex.seekAbbrevId(key) <= 0
-					? objIndex.readIndex()
-					: ((blocksIn(objOffset, objEnd) - 1) * blockSize);
-			BlockReader b = readBlock(blockOffset, objEnd);
-			b.seekAbbrevId(key);
-			while (b.next()) {
-				b.parseKey();
-				if (b.matchAbbrevId(key)) {
-					blockList = b.readBlockList();
-					break;
-				}
-				b.skipValue();
-			}
-			if (blockList == null) {
-				blockList = new IntList(0);
-			}
-			if (blockList.size() > 0) {
-				int blockIdx = blockList.get(listIdx++);
-				block = readBlock(blockIdx * blockSize, scanEnd);
-			}
-		}
-
-		void initScan() throws IOException {
-			block = readBlock(0, scanEnd);
-		}
-
-		@Override
-		public boolean next() throws IOException {
-			for (;;) {
-				if (block == null || block.type() != REF_BLOCK_TYPE) {
-					return false;
-				} else if (!block.next()) {
-					long p;
-					if (blockList != null) {
-						if (listIdx >= blockList.size()) {
-							return false;
-						}
-						int blockIdx = blockList.get(listIdx++);
-						p = blockIdx * blockSize;
-					} else {
-						p = block.endPosition();
-					}
-					if (p >= scanEnd) {
-						return false;
-					}
-					block = readBlock(p, scanEnd);
-					continue;
-				}
-
-				block.parseKey();
-				ref = block.readRef();
-				ObjectId id = ref.getObjectId();
-				if (id != null && match.equals(id)
-						&& (includeDeletes || !wasDeleted())) {
-					return true;
-				}
-			}
-		}
-
-		@Override
-		public Ref getRef() {
-			return ref;
-		}
-
-		@Override
-		public void close() {
-			// Do nothing.
-		}
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
index d270ea9..9dd6dd7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
@@ -43,12 +43,10 @@
 
 package org.eclipse.jgit.internal.storage.reftable;
 
-import static java.lang.Math.log;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_FOOTER_LEN;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_MAGIC;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
-import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.MAX_BLOCK_SIZE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
 import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VERSION_1;
@@ -65,11 +63,11 @@
 import java.util.zip.CRC32;
 
 import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.Entry;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.LogEntry;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.ObjEntry;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.RefEntry;
-import org.eclipse.jgit.internal.storage.reftable.BlockWriter.TextEntry;
+import org.eclipse.jgit.internal.storage.reftable.ChunkWriter.Entry;
+import org.eclipse.jgit.internal.storage.reftable.ChunkWriter.IndexEntry;
+import org.eclipse.jgit.internal.storage.reftable.ChunkWriter.ObjEntry;
+import org.eclipse.jgit.internal.storage.reftable.ChunkWriter.RefEntry;
+import org.eclipse.jgit.internal.storage.reftable.ChunkWriter.TextEntry;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
@@ -96,33 +94,28 @@
 
 	private long minUpdateIndex;
 	private long maxUpdateIndex;
+	private long refRootChunkAddr;
+	private long objRootChunkAddr;
 
 	private ReftableOutputStream out;
 	private ObjectIdSubclassMap<RefList> obj2ref;
 
-	private BlockWriter refIndex;
-	private BlockWriter objIndex;
-	private BlockWriter logIndex;
-	private BlockWriter cur;
-
-	private long objOffset;
-	private long logOffset;
-	private long refIndexOffset;
-	private long objIndexOffset;
-	private long logIndexOffset;
+	private List<IndexEntry> refIndex;
+	private List<IndexEntry> objIndex;
+	private ChunkWriter cur;
 
 	private long refCnt;
 	private int objCnt;
 	private long logCnt;
 	private long refBytes;
 	private long objBytes;
-	private long logBytes;
 	private int refBlocks;
 	private int objBlocks;
-	private int logBlocks;
 	private int refIndexSize;
 	private int objIndexSize;
 	private int objIdLen;
+
+	private int indexLevels;
 	private Stats stats;
 
 	/** Initialize a writer with a default configuration. */
@@ -203,7 +196,7 @@
 			restartInterval = refBlockSize < (60 << 10) ? 16 : 64;
 		}
 
-		refIndex = newIndex(refBlockSize);
+		refIndex = new ArrayList<>();
 		out = new ReftableOutputStream(os, refBlockSize);
 		if (indexObjects) {
 			obj2ref = new ObjectIdSubclassMap<>();
@@ -300,53 +293,28 @@
 	public void writeLog(String ref, long updateIndex, PersonIdent who,
 			ObjectId oldId, ObjectId newId, @Nullable String message)
 					throws IOException {
-		String msg = message != null ? message : ""; //$NON-NLS-1$
-		beginLog();
-		logCnt++;
-		write(logIndex, new LogEntry(ref, updateIndex, who, oldId, newId, msg));
 	}
 
-	private void beginLog() throws IOException {
-		if (logOffset == 0) {
-			finishRef(); // close prior ref blocks and their index, if present.
-			out.flushFileHeader();
-
-			if (logBlockSize == 0) {
-				logBlockSize = refBlockSize * 2;
-			}
-			logIndex = newIndex(logBlockSize);
-			out.setBlockSize(logBlockSize);
-			logOffset = out.size();
-		}
-	}
-
-	private int write(BlockWriter idx, BlockWriter.Entry entry)
+	private int write(List<IndexEntry> idx, ChunkWriter.Entry entry)
 			throws IOException {
 		if (cur == null) {
 			beginBlock(entry);
 		} else if (!cur.tryAdd(entry)) {
-			idx.addIndex(cur.lastKey(), out.size());
+			idx.add(new IndexEntry(cur.lastKey(), out.size()));
 			cur.writeTo(out);
-			if (cur.padBetweenBlocks()) {
-				out.padBetweenBlocksToNextBlock();
-			}
 			beginBlock(entry);
 		}
 		return out.blockCount();
 	}
 
-	private void beginBlock(BlockWriter.Entry entry)
+	private void beginBlock(ChunkWriter.Entry entry)
 			throws BlockSizeTooSmallException {
 		byte type = entry.blockType();
 		int bs = out.bytesAvailableInBlock();
-		cur = new BlockWriter(type, bs, restartInterval);
+		cur = new ChunkWriter(type, bs);
 		cur.addFirst(entry);
 	}
 
-	private BlockWriter newIndex(int bs) {
-		return new BlockWriter(INDEX_BLOCK_TYPE, bs, restartInterval);
-	}
-
 	/**
 	 * @return an estimate of the current size in bytes of the reftable, if it
 	 *         was finished right now. The estimate is only accurate if the
@@ -354,30 +322,7 @@
 	 *         to {@code false}.
 	 */
 	public long estimateTotalBytes() {
-		long bytes = out.size();
-		if (bytes == 0) {
-			bytes += FILE_HEADER_LEN;
-		}
-		if (cur != null) {
-			long offset = out.size();
-			int sz = cur.currentSize();
-			bytes += sz;
-
-			BlockWriter idx = null;
-			if (cur.blockType() == REF_BLOCK_TYPE) {
-				idx = refIndex;
-			} else if (cur.blockType() == LOG_BLOCK_TYPE) {
-				idx = logIndex;
-			}
-			if (idx != null && shouldHaveIndex(idx)) {
-				if (idx == refIndex) {
-					bytes += out.estimatePadBetweenBlocks(sz);
-				}
-				bytes += idx.estimateIndexSizeIfAdding(cur.lastKey(), offset);
-			}
-		}
-		bytes += FILE_FOOTER_LEN;
-		return bytes;
+		return 0;
 	}
 
 	/**
@@ -389,12 +334,10 @@
 	 */
 	public ReftableWriter finish() throws IOException {
 		finishRef();
-		finishLog();
 		writeFileFooter();
 
-		stats = new Stats(this, out, refIndex);
+		stats = new Stats(this, out);
 		refIndex = null;
-		logIndex = null;
 		cur = null;
 		out = null;
 		obj2ref = null;
@@ -403,14 +346,18 @@
 
 	private void finishRef() throws IOException {
 		if (cur != null && cur.blockType() == REF_BLOCK_TYPE) {
-			refBlocks = out.blockCount() + 1;
-			refIndexOffset = finishBlockMaybeWriteIndex(refIndex);
-			if (refIndexOffset > 0) {
-				refIndexSize = (int) (out.size() - refIndexOffset);
+			refIndex.add(new IndexEntry(cur.lastKey(), out.size()));
+			cur.writeTo(out);
+			cur = null;
+
+			refBlocks = out.blockCount();
+			refRootChunkAddr = writeIndex(refIndex);
+			if (refRootChunkAddr > 0) {
+				refIndexSize = (int) (out.size() - refRootChunkAddr);
 			}
 			refBytes = out.size();
 
-			if (indexObjects && !obj2ref.isEmpty() && refIndexOffset > 0) {
+			if (indexObjects && !obj2ref.isEmpty()) {
 				writeObjBlocks();
 			}
 			obj2ref = null;
@@ -422,76 +369,67 @@
 		obj2ref = null;
 		objIdLen = shortestUniqueAbbreviation(sorted);
 
-		out.padBetweenBlocksToNextBlock();
 		objCnt = sorted.size();
-		objOffset = out.size();
-		objIndex = newIndex(refBlockSize);
+		objIndex = new ArrayList<>();
 		for (RefList l : sorted) {
 			write(objIndex, new ObjEntry(objIdLen, l, l.blockIds));
 		}
 		objBlocks = (out.blockCount() + 1) - refBlocks;
-		objIndexOffset = finishBlockMaybeWriteIndex(objIndex);
-		if (objIndexOffset > 0) {
-			objIndexSize = (int) (out.size() - objIndexOffset);
+		objRootChunkAddr = 0;
+		if (objRootChunkAddr > 0) {
+			objIndexSize = (int) (out.size() - objRootChunkAddr);
 		}
-		objBytes = out.size() - objOffset;
+		objBytes = out.size();
 	}
 
-	private void finishLog() throws IOException {
-		if (cur != null && cur.blockType() == LOG_BLOCK_TYPE) {
-			logBlocks = (out.blockCount() + 1) - (refBlocks + objBlocks);
-			logIndexOffset = finishBlockMaybeWriteIndex(logIndex);
-			logBytes = out.size() - logOffset;
-		}
-	}
+	private long writeIndex(List<IndexEntry> idx) throws IOException {
+		long addr = 0;
+		while (!idx.isEmpty()) {
+			addr = out.size();
+			List<IndexEntry> l2 = new ArrayList<>();
+			int bs = out.bytesAvailableInBlock();
+			ChunkWriter c = new ChunkWriter(INDEX_BLOCK_TYPE, bs);
+			for (IndexEntry e : idx) {
+				if (!c.tryAdd(e.forIndexAt(addr))) {
+					l2.add(new IndexEntry(c.lastKey(), addr));
+					c.writeTo(out);
 
-	private long finishBlockMaybeWriteIndex(BlockWriter idx)
-			throws IOException {
-		idx.addIndex(cur.lastKey(), out.size());
-		cur.writeTo(out);
-		cur = null;
-
-		if (shouldHaveIndex(idx)) {
-			if (idx == refIndex || idx == objIndex) {
-				out.padBetweenBlocksToNextBlock();
+					addr = out.size();
+					c = new ChunkWriter(INDEX_BLOCK_TYPE, bs);
+					c.addFirst(e.forIndexAt(addr));
+				}
 			}
-			long offset = out.size();
-			idx.writeTo(out);
-			return offset;
-		} else {
-			return 0;
-		}
-	}
+			if (!l2.isEmpty()) {
+				l2.add(new IndexEntry(c.lastKey(), addr));
+			}
+			c.writeTo(out);
 
-	private boolean shouldHaveIndex(BlockWriter idx) {
-		int threshold = idx == refIndex ? 4 : 1;
-		return idx.entryCount() + (cur != null ? 1 : 0) > threshold;
+			indexLevels++;
+			idx = l2;
+		}
+		return addr;
 	}
 
 	private void writeFileHeader() throws IOException {
 		byte[] hdr = new byte[FILE_HEADER_LEN];
 		encodeHeader(hdr);
-		out.write(hdr);
+		out.write(hdr, 0, FILE_HEADER_LEN);
+		out.flushFileHeader();
 	}
 
 	private void encodeHeader(byte[] hdr) {
 		System.arraycopy(FILE_HEADER_MAGIC, 0, hdr, 0, 4);
-		NB.encodeInt32(hdr, 4, (VERSION_1 << 24) | refBlockSize);
+		NB.encodeInt32(hdr, 4, (VERSION_1 << 24));
 		NB.encodeInt64(hdr, 8, minUpdateIndex);
 		NB.encodeInt64(hdr, 16, maxUpdateIndex);
+		NB.encodeInt64(hdr, 24, refRootChunkAddr);
+		NB.encodeInt64(hdr, 32, objRootChunkAddr);
 	}
 
 	private void writeFileFooter() throws IOException {
 		int ftrLen = FILE_FOOTER_LEN;
 		byte[] ftr = new byte[ftrLen];
 		encodeHeader(ftr);
-
-		NB.encodeInt64(ftr, 24, refIndexOffset);
-		NB.encodeInt64(ftr, 32, objOffset);
-		NB.encodeInt64(ftr, 40, objIndexOffset);
-		NB.encodeInt64(ftr, 48, logOffset);
-		NB.encodeInt64(ftr, 56, logIndexOffset);
-
 		CRC32 crc = new CRC32();
 		crc.update(ftr, 0, ftrLen - 4);
 		NB.encodeInt32(ftr, ftrLen - 4, (int) crc.getValue());
@@ -520,18 +458,19 @@
 		private final long logCnt;
 		private final long refBytes;
 		private final long objBytes;
-		private final long logBytes;
+		private long logBytes;
 		private final long paddingUsed;
 		private final long totalBytes;
 		private final int refBlocks;
 		private final int objBlocks;
-		private final int logBlocks;
+		private int logBlocks;
 
-		private final int refIndexKeys;
+		private int refIndexKeys;
+		private final int indexLevels;
 		private final int refIndexSize;
 		private final int objIndexSize;
 
-		Stats(ReftableWriter w, ReftableOutputStream o, BlockWriter refIdx) {
+		Stats(ReftableWriter w, ReftableOutputStream o) {
 			refBlockSize = w.refBlockSize;
 			logBlockSize = w.logBlockSize;
 			restartInterval = w.restartInterval;
@@ -545,14 +484,12 @@
 			logCnt = w.logCnt;
 			refBytes = w.refBytes;
 			objBytes = w.objBytes;
-			logBytes = w.logBytes;
 			paddingUsed = o.paddingUsed();
 			totalBytes = o.size();
 			refBlocks = w.refBlocks;
 			objBlocks = w.objBlocks;
-			logBlocks = w.logBlocks;
+			indexLevels = w.indexLevels;
 
-			refIndexKeys = w.refIndexOffset > 0 ? refIdx.entryCount() : 0;
 			refIndexSize = w.refIndexSize;
 			objIndexSize = w.objIndexSize;
 		}
@@ -663,10 +600,7 @@
 
 		/** @return estimated number of disk seeks per ref read. */
 		public double diskSeeksPerRead() {
-			if (refIndexKeys() > 0) {
-				return 1;
-			}
-			return log(refBlockCount()) / log(2);
+			return indexLevels + 1;
 		}
 	}