Mercurial > jhg
comparison src/com/tmate/hgkit/ll/RevlogStream.java @ 5:fc265ddeab26
File content and non-effective, although working, patch application
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Tue, 21 Dec 2010 05:11:06 +0100 |
| parents | aa1912c70b36 |
| children | 5abe5af181bd |
comparison
equal
deleted
inserted
replaced
| 4:aa1912c70b36 | 5:fc265ddeab26 |
|---|---|
| 1 /** | 1 /** |
| 2 * Copyright (c) 2010 Artem Tikhomirov | 2 * Copyright (c) 2010 Artem Tikhomirov |
| 3 */ | 3 */ |
| 4 package com.tmate.hgkit.ll; | 4 package com.tmate.hgkit.ll; |
| 5 | |
| 6 import static com.tmate.hgkit.ll.HgRepository.TIP; | |
| 5 | 7 |
| 6 import java.io.BufferedInputStream; | 8 import java.io.BufferedInputStream; |
| 7 import java.io.DataInput; | 9 import java.io.DataInput; |
| 8 import java.io.DataInputStream; | 10 import java.io.DataInputStream; |
| 9 import java.io.EOFException; | 11 import java.io.EOFException; |
| 72 initOutline(); | 74 initOutline(); |
| 73 final int indexSize = index.size(); | 75 final int indexSize = index.size(); |
| 74 if (indexSize == 0) { | 76 if (indexSize == 0) { |
| 75 return; | 77 return; |
| 76 } | 78 } |
| 77 if (end == -1 /*FIXME TIP*/) { | 79 if (end == TIP) { |
| 78 end = indexSize - 1; | 80 end = indexSize - 1; |
| 81 } | |
| 82 if (start == TIP) { | |
| 83 start = indexSize - 1; | |
| 79 } | 84 } |
| 80 if (start < 0 || start >= indexSize) { | 85 if (start < 0 || start >= indexSize) { |
| 81 throw new IllegalArgumentException("Bad left range boundary " + start); | 86 throw new IllegalArgumentException("Bad left range boundary " + start); |
| 82 } | 87 } |
| 83 if (end < start || end >= indexSize) { | 88 if (end < start || end >= indexSize) { |
| 89 diIndex = getIndexStream(); | 94 diIndex = getIndexStream(); |
| 90 if (needData && !inline) { | 95 if (needData && !inline) { |
| 91 diData = getDataStream(); | 96 diData = getDataStream(); |
| 92 } | 97 } |
| 93 try { | 98 try { |
| 94 int skipped = diIndex.skipBytes(inline ? (int) index.get(start).offset : start * 64); | |
| 95 byte[] lastData = null; | 99 byte[] lastData = null; |
| 96 for (int i = start; i <= end; i++ ) { | 100 int i; |
| 97 IndexEntry ie = index.get(i); | 101 boolean extraReadsToBaseRev = false; |
| 102 if (needData && index.get(start).baseRevision < start) { | |
| 103 i = index.get(start).baseRevision; | |
| 104 extraReadsToBaseRev = true; | |
| 105 } else { | |
| 106 i = start; | |
| 107 } | |
| 108 diIndex.skipBytes(inline ? (int) index.get(i).offset : start * 64); | |
| 109 for (; i <= end; i++ ) { | |
| 98 long l = diIndex.readLong(); | 110 long l = diIndex.readLong(); |
| 99 long offset = l >>> 16; | 111 long offset = l >>> 16; |
| 100 int flags = (int) (l & 0X0FFFF); | 112 int flags = (int) (l & 0X0FFFF); |
| 101 int compressedLen = diIndex.readInt(); | 113 int compressedLen = diIndex.readInt(); |
| 102 int actualLen = diIndex.readInt(); | 114 int actualLen = diIndex.readInt(); |
| 112 if (needData) { | 124 if (needData) { |
| 113 byte[] dataBuf = new byte[compressedLen]; | 125 byte[] dataBuf = new byte[compressedLen]; |
| 114 if (inline) { | 126 if (inline) { |
| 115 diIndex.readFully(dataBuf); | 127 diIndex.readFully(dataBuf); |
| 116 } else { | 128 } else { |
| 117 diData.skipBytes((int) ie.offset); // FIXME not skip but seek!!! (skip would work only for the first time) | 129 diData.skipBytes((int) index.get(i).offset); // FIXME not skip but seek!!! (skip would work only for the first time) |
| 118 diData.readFully(dataBuf); | 130 diData.readFully(dataBuf); |
| 119 } | 131 } |
| 120 if (dataBuf[0] == 0x78 /* 'x' */) { | 132 if (dataBuf[0] == 0x78 /* 'x' */) { |
| 121 try { | 133 try { |
| 122 Inflater zlib = new Inflater(); | 134 Inflater zlib = new Inflater(); |
| 143 // this is a patch | 155 // this is a patch |
| 144 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); | 156 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); |
| 145 int patchElementIndex = 0; | 157 int patchElementIndex = 0; |
| 146 do { | 158 do { |
| 147 final int x = patchElementIndex; // shorthand | 159 final int x = patchElementIndex; // shorthand |
| 148 int p1 = (data[x] << 24) | (data[x+1] << 16) | (data[x+2] << 8) | data[x+3]; | 160 int p1 = ((data[x] & 0xFF)<< 24) | ((data[x+1] & 0xFF) << 16) | ((data[x+2] & 0xFF) << 8) | (data[x+3] & 0xFF); |
| 149 int p2 = (data[x+4] << 24) | (data[x+5] << 16) | (data[x+6] << 8) | data[x+7]; | 161 int p2 = ((data[x+4] & 0xFF) << 24) | ((data[x+5] & 0xFF) << 16) | ((data[x+6] & 0xFF) << 8) | (data[x+7] & 0xFF); |
| 150 int len = (data[x+8] << 24) | (data[x+9] << 16) | (data[x+10] << 8) | data[x+11]; | 162 int len = ((data[x+8] & 0xFF) << 24) | ((data[x+9] & 0xFF) << 16) | ((data[x+10] & 0xFF) << 8) | (data[x+11] & 0xFF); |
| 151 patchElementIndex += 12 + len; | 163 patchElementIndex += 12 + len; |
| 152 patches.add(new PatchRecord(p1, p2, len, data, x+12)); | 164 patches.add(new PatchRecord(p1, p2, len, data, x+12)); |
| 153 } while (patchElementIndex < data.length); | 165 } while (patchElementIndex < data.length); |
| 154 // | 166 // |
| 155 byte[] baseRevContent; | 167 byte[] baseRevContent = lastData; |
| 156 if (baseRevision == i - 1) { | |
| 157 baseRevContent = lastData; | |
| 158 } else { | |
| 159 // FIXME implement delta collection from few revisions | |
| 160 // read baseRevision plus all deltas between this revision and base. Need to do this effectively. | |
| 161 throw HgRepository.notImplemented(); | |
| 162 } | |
| 163 | |
| 164 // FIXME need to collect all patches between baseRevision and current version | |
| 165 data = apply(baseRevContent, patches); | 168 data = apply(baseRevContent, patches); |
| 166 } | 169 } |
| 167 } else { | 170 } else { |
| 168 if (inline) { | 171 if (inline) { |
| 169 diIndex.skipBytes(compressedLen); | 172 diIndex.skipBytes(compressedLen); |
| 170 } | 173 } |
| 171 } | 174 } |
| 172 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, buf, data); | 175 if (!extraReadsToBaseRev || i >= start) { |
| 176 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, buf, data); | |
| 177 } | |
| 173 lastData = data; | 178 lastData = data; |
| 174 } | 179 } |
| 175 } catch (EOFException ex) { | 180 } catch (EOFException ex) { |
| 176 // should not happen as long as we read inside known boundaries | 181 // should not happen as long as we read inside known boundaries |
| 177 throw new IllegalStateException(ex); | 182 throw new IllegalStateException(ex); |
| 194 final int INLINEDATA = 1 << 16; | 199 final int INLINEDATA = 1 << 16; |
| 195 inline = (versionField & INLINEDATA) != 0; | 200 inline = (versionField & INLINEDATA) != 0; |
| 196 long offset = 0; // first offset is always 0, thus Hg uses it for other purposes | 201 long offset = 0; // first offset is always 0, thus Hg uses it for other purposes |
| 197 while(true) { // EOFExcepiton should get us outta here. FIXME Our inputstream should has explicit no-more-data indicator | 202 while(true) { // EOFExcepiton should get us outta here. FIXME Our inputstream should has explicit no-more-data indicator |
| 198 int compressedLen = di.readInt(); | 203 int compressedLen = di.readInt(); |
| 199 // 8+4 = 12 bytes total read | 204 // 8+4 = 12 bytes total read here |
| 200 // int actualLen = di.readInt(); | 205 int actualLen = di.readInt(); |
| 201 // int baseRevision = di.readInt(); | 206 int baseRevision = di.readInt(); |
| 207 // 12 + 8 = 20 bytes read here | |
| 202 // int linkRevision = di.readInt(); | 208 // int linkRevision = di.readInt(); |
| 203 // int parent1Revision = di.readInt(); | 209 // int parent1Revision = di.readInt(); |
| 204 // int parent2Revision = di.readInt(); | 210 // int parent2Revision = di.readInt(); |
| 205 // byte[] nodeid = new byte[32]; | 211 // byte[] nodeid = new byte[32]; |
| 206 if (inline) { | 212 if (inline) { |
| 207 res.add(new IndexEntry(offset + 64*res.size(), compressedLen)); | 213 res.add(new IndexEntry(offset + 64*res.size(), baseRevision)); |
| 208 di.skipBytes(5*4 + 32 + compressedLen); // Check: 52 (skip) + 12 (read) = 64 (total RevlogNG record size) | 214 di.skipBytes(3*4 + 32 + compressedLen); // Check: 44 (skip) + 20 (read) = 64 (total RevlogNG record size) |
| 209 } else { | 215 } else { |
| 210 res.add(new IndexEntry(offset, compressedLen)); | 216 res.add(new IndexEntry(offset, baseRevision)); |
| 211 di.skipBytes(5*4 + 32); | 217 di.skipBytes(3*4 + 32); |
| 212 } | 218 } |
| 213 long l = di.readLong(); | 219 long l = di.readLong(); |
| 214 offset = l >>> 16; | 220 offset = l >>> 16; |
| 215 } | 221 } |
| 216 } catch (EOFException ex) { | 222 } catch (EOFException ex) { |
| 239 } | 245 } |
| 240 } | 246 } |
| 241 | 247 |
| 242 | 248 |
| 243 // perhaps, package-local or protected, if anyone else from low-level needs them | 249 // perhaps, package-local or protected, if anyone else from low-level needs them |
| 250 // XXX think over if we should keep offset in case of separate data file - we read the field anyway. Perhaps, distinct entry classes for Inline and non-inline indexes? | |
| 244 private static class IndexEntry { | 251 private static class IndexEntry { |
| 245 public final long offset; // for separate .i and .d - copy of index record entry, for inline index - actual offset of the record in the .i file (record entry + revision * record size)) | 252 public final long offset; // for separate .i and .d - copy of index record entry, for inline index - actual offset of the record in the .i file (record entry + revision * record size)) |
| 246 public final int length; // data past fixed record (need to decide whether including header size or not), and whether length is of compressed data or not | 253 //public final int length; // data past fixed record (need to decide whether including header size or not), and whether length is of compressed data or not |
| 247 | 254 public final int baseRevision; |
| 248 public IndexEntry(long o, int l) { | 255 |
| 256 public IndexEntry(long o, int baseRev) { | |
| 249 offset = o; | 257 offset = o; |
| 250 length = l; | 258 baseRevision = baseRev; |
| 251 } | 259 } |
| 252 } | 260 } |
| 253 | 261 |
| 254 // mpatch.c : apply() | 262 // mpatch.c : apply() |
| 255 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF | 263 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF |
