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