tikhomirov@22: /* tikhomirov@394: * Copyright (c) 2010-2012 TMate Software Ltd tikhomirov@73: * tikhomirov@73: * This program is free software; you can redistribute it and/or modify tikhomirov@73: * it under the terms of the GNU General Public License as published by tikhomirov@73: * the Free Software Foundation; version 2 of the License. tikhomirov@73: * tikhomirov@73: * This program is distributed in the hope that it will be useful, tikhomirov@73: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@73: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@73: * GNU General Public License for more details. tikhomirov@73: * tikhomirov@73: * For information on how to redistribute this software under tikhomirov@73: * the terms of a license other than GNU General Public License tikhomirov@102: * contact TMate Software at support@hg4j.com tikhomirov@22: */ tikhomirov@73: package org.tmatesoft.hg.internal; tikhomirov@0: tikhomirov@0: import java.io.BufferedInputStream; tikhomirov@392: import java.io.ByteArrayInputStream; tikhomirov@0: import java.io.DataInput; tikhomirov@0: import java.io.DataInputStream; tikhomirov@0: import java.io.File; tikhomirov@0: import java.io.FileInputStream; tikhomirov@392: import java.io.IOException; tikhomirov@392: import java.io.UnsupportedEncodingException; tikhomirov@0: import java.math.BigInteger; tikhomirov@376: import java.nio.ByteBuffer; tikhomirov@376: import java.nio.channels.FileChannel; tikhomirov@0: import java.util.zip.Inflater; tikhomirov@0: tikhomirov@0: /** tikhomirov@73: * Utility to test/debug/troubleshoot tikhomirov@73: * tikhomirov@73: * @author Artem Tikhomirov tikhomirov@73: * @author TMate Software Ltd. tikhomirov@0: */ tikhomirov@73: public class RevlogDump { tikhomirov@0: tikhomirov@73: /** tikhomirov@73: * Takes 3 command line arguments - tikhomirov@73: * repository path, tikhomirov@73: * path to index file (i.e. store/data/hello.c.i) in the repository (relative) tikhomirov@73: * and "dumpData" whether to print actual content or just revlog headers tikhomirov@73: */ tikhomirov@0: public static void main(String[] args) throws Exception { tikhomirov@73: String repo = "/temp/hg/hello/.hg/"; tikhomirov@73: String filename = "store/00changelog.i"; tikhomirov@22: // String filename = "store/data/hello.c.i"; tikhomirov@4: // String filename = "store/data/docs/readme.i"; tikhomirov@392: boolean dumpDataFull = true; tikhomirov@392: boolean dumpDataStats = false; tikhomirov@88: if (args.length > 1) { tikhomirov@73: repo = args[0]; tikhomirov@73: filename = args[1]; tikhomirov@392: dumpDataFull = args.length > 2 ? "dumpData".equals(args[2]) : false; tikhomirov@392: dumpDataStats = args.length > 2 ? "dumpDataStats".equals(args[2]) : false; tikhomirov@73: } tikhomirov@392: final boolean needRevData = dumpDataFull || dumpDataStats; tikhomirov@0: // tikhomirov@376: DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(repo, filename)))); tikhomirov@0: DataInput di = dis; tikhomirov@0: dis.mark(10); tikhomirov@0: int versionField = di.readInt(); tikhomirov@0: dis.reset(); tikhomirov@0: final int INLINEDATA = 1 << 16; tikhomirov@0: tikhomirov@376: final boolean inlineData = (versionField & INLINEDATA) != 0; tikhomirov@0: System.out.printf("%#8x, inline: %b\n", versionField, inlineData); tikhomirov@376: FileChannel dataStream = null; tikhomirov@392: if (!inlineData && needRevData) { tikhomirov@376: dataStream = new FileInputStream(new File(repo, filename.substring(0, filename.length()-2) + ".d")).getChannel(); tikhomirov@376: } tikhomirov@73: System.out.println("Index Offset Flags Packed Actual Base Rev Link Rev Parent1 Parent2 nodeid"); tikhomirov@376: int entryIndex = 0; tikhomirov@0: while (dis.available() > 0) { tikhomirov@0: long l = di.readLong(); tikhomirov@376: long offset = entryIndex == 0 ? 0 : (l >>> 16); tikhomirov@420: int flags = (int) (l & 0x0FFFF); tikhomirov@0: int compressedLen = di.readInt(); tikhomirov@0: int actualLen = di.readInt(); tikhomirov@0: int baseRevision = di.readInt(); tikhomirov@0: int linkRevision = di.readInt(); tikhomirov@0: int parent1Revision = di.readInt(); tikhomirov@0: int parent2Revision = di.readInt(); tikhomirov@0: byte[] buf = new byte[32]; tikhomirov@0: di.readFully(buf, 12, 20); tikhomirov@168: dis.skipBytes(12); tikhomirov@168: // CAN'T USE skip() here without extra precautions. E.g. I ran into situation when tikhomirov@168: // buffer was 8192 and BufferedInputStream was at position 8182 before attempt to skip(12). tikhomirov@168: // BIS silently skips available bytes and leaves me two extra bytes that ruin the rest of the code. tikhomirov@376: System.out.printf("%4d:%14d %6X %10d %10d %10d %10d %8d %8d %040x\n", entryIndex, offset, flags, compressedLen, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, new BigInteger(buf)); tikhomirov@376: String resultString; tikhomirov@376: byte[] data = new byte[compressedLen]; tikhomirov@0: if (inlineData) { tikhomirov@0: di.readFully(data); tikhomirov@392: } else if (needRevData) { tikhomirov@376: dataStream.position(offset); tikhomirov@376: dataStream.read(ByteBuffer.wrap(data)); tikhomirov@376: } tikhomirov@392: if (needRevData) { tikhomirov@376: if (compressedLen == 0) { tikhomirov@376: resultString = ""; tikhomirov@3: } else { tikhomirov@376: if (data[0] == 0x78 /* 'x' */) { tikhomirov@376: Inflater zlib = new Inflater(); tikhomirov@376: zlib.setInput(data, 0, compressedLen); tikhomirov@376: byte[] result = new byte[actualLen*2]; tikhomirov@376: int resultLen = zlib.inflate(result); tikhomirov@376: zlib.end(); tikhomirov@392: resultString = buildString(result, 0, resultLen, baseRevision != entryIndex, dumpDataFull); tikhomirov@376: } else if (data[0] == 0x75 /* 'u' */) { tikhomirov@392: resultString = buildString(data, 1, data.length - 1, baseRevision != entryIndex, dumpDataFull); tikhomirov@376: } else { tikhomirov@392: resultString = buildString(data, 0, data.length, baseRevision != entryIndex, dumpDataFull); tikhomirov@376: } tikhomirov@3: } tikhomirov@376: System.out.println(resultString); tikhomirov@0: } tikhomirov@376: entryIndex++; tikhomirov@0: } tikhomirov@0: dis.close(); tikhomirov@376: if (dataStream != null) { tikhomirov@376: dataStream.close(); tikhomirov@376: } tikhomirov@0: // tikhomirov@0: } tikhomirov@392: tikhomirov@392: private static String buildString(byte[] data, int offset, int len, boolean isPatch, boolean completeDataDump) throws IOException, UnsupportedEncodingException { tikhomirov@392: if (isPatch) { tikhomirov@392: DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data, offset, len)); tikhomirov@392: StringBuilder sb = new StringBuilder(); tikhomirov@392: sb.append(":\n"); tikhomirov@392: while (dis.available() > 0) { tikhomirov@392: int s = dis.readInt(); tikhomirov@392: int e = dis.readInt(); tikhomirov@392: int l = dis.readInt(); tikhomirov@392: sb.append(String.format("%d..%d, %d", s, e, l)); tikhomirov@392: if (completeDataDump) { tikhomirov@392: byte[] src = new byte[l]; tikhomirov@392: dis.read(src, 0, l); tikhomirov@392: sb.append(":"); tikhomirov@392: sb.append(new String(src, 0, l, "UTF-8")); tikhomirov@392: } else { tikhomirov@392: dis.skipBytes(l); tikhomirov@392: } tikhomirov@392: sb.append('\n'); tikhomirov@392: } tikhomirov@392: return sb.toString(); tikhomirov@392: } else { tikhomirov@392: if (completeDataDump) { tikhomirov@392: return new String(data, offset, len, "UTF-8"); tikhomirov@392: } tikhomirov@392: return String.format(":%d bytes", len-offset); tikhomirov@392: } tikhomirov@392: } tikhomirov@0: }