Mercurial > jhg
comparison src/com/tmate/hgkit/ll/RevlogStream.java @ 51:9429c7bd1920 wrap-data-access
Try DataAccess to reach revision data instead of plain byte arrays
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Sun, 16 Jan 2011 01:20:26 +0100 |
| parents | 26e3eeaa3962 |
| children | a6f39e595b2b |
comparison
equal
deleted
inserted
replaced
| 50:f1db8610da62 | 51:9429c7bd1920 |
|---|---|
| 9 import java.io.IOException; | 9 import java.io.IOException; |
| 10 import java.util.ArrayList; | 10 import java.util.ArrayList; |
| 11 import java.util.Collections; | 11 import java.util.Collections; |
| 12 import java.util.LinkedList; | 12 import java.util.LinkedList; |
| 13 import java.util.List; | 13 import java.util.List; |
| 14 import java.util.zip.DataFormatException; | 14 |
| 15 import java.util.zip.Inflater; | 15 import com.tmate.hgkit.fs.ByteArrayDataAccess; |
| 16 | |
| 17 import com.tmate.hgkit.fs.DataAccess; | 16 import com.tmate.hgkit.fs.DataAccess; |
| 18 import com.tmate.hgkit.fs.DataAccessProvider; | 17 import com.tmate.hgkit.fs.DataAccessProvider; |
| 18 import com.tmate.hgkit.fs.FilterDataAccess; | |
| 19 import com.tmate.hgkit.fs.InflaterDataAccess; | |
| 19 | 20 |
| 20 /** | 21 /** |
| 21 * ? Single RevlogStream per file per repository with accessor to record access session (e.g. with back/forward operations), | 22 * ? Single RevlogStream per file per repository with accessor to record access session (e.g. with back/forward operations), |
| 22 * or numerous RevlogStream with separate representation of the underlaying data (cached, lazy ChunkStream)? | 23 * or numerous RevlogStream with separate representation of the underlaying data (cached, lazy ChunkStream)? |
| 23 * @author artem | 24 * @author artem |
| 133 if (needData && !inline) { | 134 if (needData && !inline) { |
| 134 daData = getDataStream(); | 135 daData = getDataStream(); |
| 135 } | 136 } |
| 136 try { | 137 try { |
| 137 byte[] nodeidBuf = new byte[20]; | 138 byte[] nodeidBuf = new byte[20]; |
| 138 byte[] lastData = null; | 139 DataAccess lastUserData = null; |
| 139 int i; | 140 int i; |
| 140 boolean extraReadsToBaseRev = false; | 141 boolean extraReadsToBaseRev = false; |
| 141 if (needData && index.get(start).baseRevision < start) { | 142 if (needData && index.get(start).baseRevision < start) { |
| 142 i = index.get(start).baseRevision; | 143 i = index.get(start).baseRevision; |
| 143 extraReadsToBaseRev = true; | 144 extraReadsToBaseRev = true; |
| 144 } else { | 145 } else { |
| 145 i = start; | 146 i = start; |
| 146 } | 147 } |
| 147 | 148 |
| 148 daIndex.seek(inline ? (int) index.get(i).offset : i * REVLOGV1_RECORD_SIZE); | 149 daIndex.seek(inline ? index.get(i).offset : i * REVLOGV1_RECORD_SIZE); |
| 149 for (; i <= end; i++ ) { | 150 for (; i <= end; i++ ) { |
| 151 if (inline && needData) { | |
| 152 // inspector reading data (though FilterDataAccess) may have affected index position | |
| 153 daIndex.seek(index.get(i).offset); | |
| 154 } | |
| 150 long l = daIndex.readLong(); | 155 long l = daIndex.readLong(); |
| 151 @SuppressWarnings("unused") | 156 @SuppressWarnings("unused") |
| 152 long offset = l >>> 16; | 157 long offset = l >>> 16; |
| 153 @SuppressWarnings("unused") | 158 @SuppressWarnings("unused") |
| 154 int flags = (int) (l & 0X0FFFF); | 159 int flags = (int) (l & 0X0FFFF); |
| 159 int parent1Revision = daIndex.readInt(); | 164 int parent1Revision = daIndex.readInt(); |
| 160 int parent2Revision = daIndex.readInt(); | 165 int parent2Revision = daIndex.readInt(); |
| 161 // Hg has 32 bytes here, uses 20 for nodeid, and keeps 12 last bytes empty | 166 // Hg has 32 bytes here, uses 20 for nodeid, and keeps 12 last bytes empty |
| 162 daIndex.readBytes(nodeidBuf, 0, 20); | 167 daIndex.readBytes(nodeidBuf, 0, 20); |
| 163 daIndex.skip(12); | 168 daIndex.skip(12); |
| 164 byte[] data = null; | 169 DataAccess userDataAccess = null; |
| 165 if (needData) { | 170 if (needData) { |
| 166 byte[] dataBuf = new byte[compressedLen]; | 171 final byte firstByte; |
| 172 long streamOffset = index.get(i).offset; | |
| 173 DataAccess streamDataAccess; | |
| 167 if (inline) { | 174 if (inline) { |
| 168 daIndex.readBytes(dataBuf, 0, compressedLen); | 175 streamDataAccess = daIndex; |
| 176 streamOffset += REVLOGV1_RECORD_SIZE; // don't need to do seek as it's actual position in the index stream | |
| 169 } else { | 177 } else { |
| 170 daData.seek(index.get(i).offset); | 178 streamDataAccess = daData; |
| 171 daData.readBytes(dataBuf, 0, compressedLen); | 179 daData.seek(streamOffset); |
| 172 } | 180 } |
| 173 if (dataBuf[0] == 0x78 /* 'x' */) { | 181 firstByte = streamDataAccess.readByte(); |
| 174 try { | 182 if (firstByte == 0x78 /* 'x' */) { |
| 175 Inflater zlib = new Inflater(); // XXX Consider reuse of Inflater, and/or stream alternative | 183 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen); |
| 176 zlib.setInput(dataBuf, 0, compressedLen); | 184 } else if (firstByte == 0x75 /* 'u' */) { |
| 177 byte[] result = new byte[actualLen*2]; // FIXME need to use zlib.finished() instead | 185 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); |
| 178 int resultLen = zlib.inflate(result); | |
| 179 zlib.end(); | |
| 180 data = new byte[resultLen]; | |
| 181 System.arraycopy(result, 0, data, 0, resultLen); | |
| 182 } catch (DataFormatException ex) { | |
| 183 ex.printStackTrace(); | |
| 184 data = new byte[0]; // FIXME need better failure strategy | |
| 185 } | |
| 186 } else if (dataBuf[0] == 0x75 /* 'u' */) { | |
| 187 data = new byte[dataBuf.length - 1]; | |
| 188 System.arraycopy(dataBuf, 1, data, 0, data.length); | |
| 189 } else { | 186 } else { |
| 190 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' | 187 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' |
| 191 // but I don't see reason not to return data as is | 188 // but I don't see reason not to return data as is |
| 192 data = dataBuf; | 189 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen); |
| 193 } | 190 } |
| 194 // XXX | 191 // XXX |
| 195 if (baseRevision != i) { // XXX not sure if this is the right way to detect a patch | 192 if (baseRevision != i) { // XXX not sure if this is the right way to detect a patch |
| 196 // this is a patch | 193 // this is a patch |
| 197 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); | 194 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); |
| 198 int patchElementIndex = 0; | 195 while (!userDataAccess.isEmpty()) { |
| 199 do { | 196 PatchRecord pr = PatchRecord.read(userDataAccess); |
| 200 PatchRecord pr = PatchRecord.read(data, patchElementIndex); | 197 System.out.printf("PatchRecord:%d %d %d\n", pr.start, pr.end, pr.len); |
| 201 patches.add(pr); | 198 patches.add(pr); |
| 202 patchElementIndex += 12 + pr.len; | 199 } |
| 203 } while (patchElementIndex < data.length); | 200 userDataAccess.done(); |
| 204 // | 201 // |
| 205 byte[] baseRevContent = lastData; | 202 byte[] userData = apply(lastUserData, actualLen, patches); |
| 206 data = apply(baseRevContent, actualLen, patches); | 203 userDataAccess = new ByteArrayDataAccess(userData); |
| 207 } | 204 } |
| 208 } else { | 205 } else { |
| 209 if (inline) { | 206 if (inline) { |
| 210 daIndex.skip(compressedLen); | 207 daIndex.skip(compressedLen); |
| 211 } | 208 } |
| 212 } | 209 } |
| 213 if (!extraReadsToBaseRev || i >= start) { | 210 if (!extraReadsToBaseRev || i >= start) { |
| 214 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeidBuf, data); | 211 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeidBuf, userDataAccess); |
| 215 } | 212 } |
| 216 lastData = data; | 213 if (userDataAccess != null) { |
| 214 userDataAccess.reset(); | |
| 215 if (lastUserData != null) { | |
| 216 lastUserData.done(); | |
| 217 } | |
| 218 lastUserData = userDataAccess; | |
| 219 } | |
| 217 } | 220 } |
| 218 } catch (IOException ex) { | 221 } catch (IOException ex) { |
| 219 throw new IllegalStateException(ex); // FIXME need better handling | 222 throw new IllegalStateException(ex); // FIXME need better handling |
| 220 } finally { | 223 } finally { |
| 221 daIndex.done(); | 224 daIndex.done(); |
| 290 } | 293 } |
| 291 } | 294 } |
| 292 | 295 |
| 293 // mpatch.c : apply() | 296 // mpatch.c : apply() |
| 294 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF | 297 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF |
| 295 /*package-local for HgBundle; until moved to better place*/static byte[] apply(byte[] baseRevisionContent, int outcomeLen, List<PatchRecord> patch) { | 298 /*package-local for HgBundle; until moved to better place*/static byte[] apply(DataAccess baseRevisionContent, int outcomeLen, List<PatchRecord> patch) throws IOException { |
| 296 int last = 0, destIndex = 0; | 299 int last = 0, destIndex = 0; |
| 297 if (outcomeLen == -1) { | 300 if (outcomeLen == -1) { |
| 298 outcomeLen = baseRevisionContent.length; | 301 outcomeLen = (int) baseRevisionContent.length(); |
| 299 for (PatchRecord pr : patch) { | 302 for (PatchRecord pr : patch) { |
| 300 outcomeLen += pr.start - last + pr.len; | 303 outcomeLen += pr.start - last + pr.len; |
| 301 last = pr.end; | 304 last = pr.end; |
| 302 } | 305 } |
| 303 outcomeLen -= last; | 306 outcomeLen -= last; |
| 304 last = 0; | 307 last = 0; |
| 305 } | 308 } |
| 309 System.out.println(baseRevisionContent.length()); | |
| 306 byte[] rv = new byte[outcomeLen]; | 310 byte[] rv = new byte[outcomeLen]; |
| 307 for (PatchRecord pr : patch) { | 311 for (PatchRecord pr : patch) { |
| 308 System.arraycopy(baseRevisionContent, last, rv, destIndex, pr.start-last); | 312 baseRevisionContent.seek(last); |
| 313 baseRevisionContent.readBytes(rv, destIndex, pr.start-last); | |
| 309 destIndex += pr.start - last; | 314 destIndex += pr.start - last; |
| 310 System.arraycopy(pr.data, 0, rv, destIndex, pr.data.length); | 315 System.arraycopy(pr.data, 0, rv, destIndex, pr.data.length); |
| 311 destIndex += pr.data.length; | 316 destIndex += pr.data.length; |
| 312 last = pr.end; | 317 last = pr.end; |
| 313 } | 318 } |
| 314 System.arraycopy(baseRevisionContent, last, rv, destIndex, baseRevisionContent.length - last); | 319 baseRevisionContent.seek(last); |
| 320 baseRevisionContent.readBytes(rv, destIndex, (int) (baseRevisionContent.length() - last)); | |
| 315 return rv; | 321 return rv; |
| 316 } | 322 } |
| 317 | 323 |
| 318 // @see http://mercurial.selenic.com/wiki/BundleFormat, in Changelog group description | 324 // @see http://mercurial.selenic.com/wiki/BundleFormat, in Changelog group description |
| 319 /*package-local*/ static class PatchRecord { // copy of struct frag from mpatch.c | 325 /*package-local*/ static class PatchRecord { // copy of struct frag from mpatch.c |
