Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 158:b413b16d10a5
Integer offsets and file length explictly, rather than casts throughout code. Inflater may benefit from total length hint, but shall calculate it by its own if needed
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 09 Mar 2011 13:16:37 +0100 |
parents | d5268ca7715b |
children | 71ddbf8603e8 |
comparison
equal
deleted
inserted
replaced
157:d5268ca7715b | 158:b413b16d10a5 |
---|---|
24 import java.util.ArrayList; | 24 import java.util.ArrayList; |
25 import java.util.Collections; | 25 import java.util.Collections; |
26 import java.util.LinkedList; | 26 import java.util.LinkedList; |
27 import java.util.List; | 27 import java.util.List; |
28 | 28 |
29 import org.tmatesoft.hg.core.HgBadStateException; | |
29 import org.tmatesoft.hg.core.Nodeid; | 30 import org.tmatesoft.hg.core.Nodeid; |
30 import org.tmatesoft.hg.repo.HgRepository; | 31 import org.tmatesoft.hg.repo.HgRepository; |
31 | 32 |
32 | 33 |
33 /** | 34 /** |
75 DataAccess daIndex = getIndexStream(); // XXX may supply a hint that I'll need really few bytes of data (although at some offset) | 76 DataAccess daIndex = getIndexStream(); // XXX may supply a hint that I'll need really few bytes of data (although at some offset) |
76 if (revision == TIP) { | 77 if (revision == TIP) { |
77 revision = indexSize - 1; | 78 revision = indexSize - 1; |
78 } | 79 } |
79 try { | 80 try { |
80 int recordOffset = inline ? (int) index.get(revision).offset : revision * REVLOGV1_RECORD_SIZE; | 81 int recordOffset = inline ? index.get(revision).getIntOffset() : revision * REVLOGV1_RECORD_SIZE; |
81 daIndex.seek(recordOffset + 12); // 6+2+4 | 82 daIndex.seek(recordOffset + 12); // 6+2+4 |
82 int actualLen = daIndex.readInt(); | 83 int actualLen = daIndex.readInt(); |
83 return actualLen; | 84 return actualLen; |
84 } catch (IOException ex) { | 85 } catch (IOException ex) { |
85 ex.printStackTrace(); // log error. FIXME better handling | 86 ex.printStackTrace(); // log error. FIXME better handling |
97 if (revision < 0 || revision >= indexSize) { | 98 if (revision < 0 || revision >= indexSize) { |
98 throw new IllegalArgumentException(Integer.toString(revision)); | 99 throw new IllegalArgumentException(Integer.toString(revision)); |
99 } | 100 } |
100 DataAccess daIndex = getIndexStream(); | 101 DataAccess daIndex = getIndexStream(); |
101 try { | 102 try { |
102 int recordOffset = inline ? (int) index.get(revision).offset : revision * REVLOGV1_RECORD_SIZE; | 103 int recordOffset = inline ? index.get(revision).getIntOffset() : revision * REVLOGV1_RECORD_SIZE; |
103 daIndex.seek(recordOffset + 32); | 104 daIndex.seek(recordOffset + 32); |
104 byte[] rv = new byte[20]; | 105 byte[] rv = new byte[20]; |
105 daIndex.readBytes(rv, 0, 20); | 106 daIndex.readBytes(rv, 0, 20); |
106 return rv; | 107 return rv; |
107 } catch (IOException ex) { | 108 } catch (IOException ex) { |
120 if (revision < 0 || revision > last) { | 121 if (revision < 0 || revision > last) { |
121 throw new IllegalArgumentException(Integer.toString(revision)); | 122 throw new IllegalArgumentException(Integer.toString(revision)); |
122 } | 123 } |
123 DataAccess daIndex = getIndexStream(); | 124 DataAccess daIndex = getIndexStream(); |
124 try { | 125 try { |
125 int recordOffset = inline ? (int) index.get(revision).offset : revision * REVLOGV1_RECORD_SIZE; | 126 int recordOffset = inline ? index.get(revision).getIntOffset() : revision * REVLOGV1_RECORD_SIZE; |
126 daIndex.seek(recordOffset + 20); | 127 daIndex.seek(recordOffset + 20); |
127 int linkRev = daIndex.readInt(); | 128 int linkRev = daIndex.readInt(); |
128 return linkRev; | 129 return linkRev; |
129 } catch (IOException ex) { | 130 } catch (IOException ex) { |
130 ex.printStackTrace(); | 131 ex.printStackTrace(); |
206 extraReadsToBaseRev = true; | 207 extraReadsToBaseRev = true; |
207 } else { | 208 } else { |
208 i = start; | 209 i = start; |
209 } | 210 } |
210 | 211 |
211 daIndex.seek(inline ? index.get(i).offset : i * REVLOGV1_RECORD_SIZE); | 212 daIndex.seek(inline ? index.get(i).getIntOffset() : i * REVLOGV1_RECORD_SIZE); |
212 for (; i <= end; i++ ) { | 213 for (; i <= end; i++ ) { |
213 if (inline && needData) { | 214 if (inline && needData) { |
214 // inspector reading data (though FilterDataAccess) may have affected index position | 215 // inspector reading data (though FilterDataAccess) may have affected index position |
215 daIndex.seek(index.get(i).offset); | 216 daIndex.seek(index.get(i).getIntOffset()); |
216 } | 217 } |
217 long l = daIndex.readLong(); // 0 | 218 long l = daIndex.readLong(); // 0 |
218 @SuppressWarnings("unused") | 219 @SuppressWarnings("unused") |
219 long offset = l >>> 16; | 220 long offset = l >>> 16; |
220 @SuppressWarnings("unused") | 221 @SuppressWarnings("unused") |
229 daIndex.readBytes(nodeidBuf, 0, 20); // +32 | 230 daIndex.readBytes(nodeidBuf, 0, 20); // +32 |
230 daIndex.skip(12); | 231 daIndex.skip(12); |
231 DataAccess userDataAccess = null; | 232 DataAccess userDataAccess = null; |
232 if (needData) { | 233 if (needData) { |
233 final byte firstByte; | 234 final byte firstByte; |
234 long streamOffset = index.get(i).offset; | 235 int streamOffset = index.get(i).getIntOffset(); |
235 DataAccess streamDataAccess; | 236 DataAccess streamDataAccess; |
236 if (inline) { | 237 if (inline) { |
237 streamDataAccess = daIndex; | 238 streamDataAccess = daIndex; |
238 streamOffset += REVLOGV1_RECORD_SIZE; // don't need to do seek as it's actual position in the index stream | 239 streamOffset += REVLOGV1_RECORD_SIZE; // don't need to do seek as it's actual position in the index stream |
239 } else { | 240 } else { |
240 streamDataAccess = daData; | 241 streamDataAccess = daData; |
241 daData.seek(streamOffset); | 242 daData.seek(streamOffset); |
242 } | 243 } |
244 final boolean patchToPrevious = baseRevision != i; // XXX not sure if this is the right way to detect a patch | |
243 firstByte = streamDataAccess.readByte(); | 245 firstByte = streamDataAccess.readByte(); |
244 if (firstByte == 0x78 /* 'x' */) { | 246 if (firstByte == 0x78 /* 'x' */) { |
245 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen); | 247 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen); |
246 } else if (firstByte == 0x75 /* 'u' */) { | 248 } else if (firstByte == 0x75 /* 'u' */) { |
247 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); | 249 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); |
248 } else { | 250 } else { |
249 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' | 251 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' |
250 // but I don't see reason not to return data as is | 252 // but I don't see reason not to return data as is |
251 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen); | 253 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen); |
252 } | 254 } |
253 // XXX | 255 // XXX |
254 if (baseRevision != i) { // XXX not sure if this is the right way to detect a patch | 256 if (patchToPrevious) { |
255 // this is a patch | 257 // this is a patch |
256 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); | 258 LinkedList<PatchRecord> patches = new LinkedList<PatchRecord>(); |
257 while (!userDataAccess.isEmpty()) { | 259 while (!userDataAccess.isEmpty()) { |
258 PatchRecord pr = PatchRecord.read(userDataAccess); | 260 PatchRecord pr = PatchRecord.read(userDataAccess); |
259 // System.out.printf("PatchRecord:%d %d %d\n", pr.start, pr.end, pr.len); | 261 // System.out.printf("PatchRecord:%d %d %d\n", pr.start, pr.end, pr.len); |
279 } | 281 } |
280 lastUserData = userDataAccess; | 282 lastUserData = userDataAccess; |
281 } | 283 } |
282 } | 284 } |
283 } catch (IOException ex) { | 285 } catch (IOException ex) { |
284 throw new IllegalStateException(ex); // FIXME need better handling | 286 throw new HgBadStateException(ex); // FIXME need better handling |
285 } finally { | 287 } finally { |
286 daIndex.done(); | 288 daIndex.done(); |
287 if (daData != null) { | 289 if (daData != null) { |
288 daData.done(); | 290 daData.done(); |
289 } | 291 } |
343 | 345 |
344 | 346 |
345 // perhaps, package-local or protected, if anyone else from low-level needs them | 347 // perhaps, package-local or protected, if anyone else from low-level needs them |
346 // 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? | 348 // 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? |
347 private static class IndexEntry { | 349 private static class IndexEntry { |
348 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)) | 350 private final int/*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)) |
349 //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 | 351 //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 |
350 public final int baseRevision; | 352 public final int baseRevision; |
351 | 353 |
352 public IndexEntry(long o, int baseRev) { | 354 public IndexEntry(long o, int baseRev) { |
353 offset = o; | 355 offset = (int) o; |
356 // Index file stores offsets as long, but since DataAccess doesn't support long length() and others yet, | |
357 // no reason to operate with long offsets | |
358 if (o != offset) { | |
359 throw new HgBadStateException("Data too big, offset didn't fit to sizeof(int)"); | |
360 } | |
354 baseRevision = baseRev; | 361 baseRevision = baseRev; |
362 } | |
363 | |
364 public int getIntOffset() { | |
365 return offset; | |
355 } | 366 } |
356 } | 367 } |
357 | 368 |
358 // mpatch.c : apply() | 369 // mpatch.c : apply() |
359 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF | 370 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF |
360 public/*for HgBundle; until moved to better place*/static byte[] apply(DataAccess baseRevisionContent, int outcomeLen, List<PatchRecord> patch) throws IOException { | 371 public/*for HgBundle; until moved to better place*/static byte[] apply(DataAccess baseRevisionContent, int outcomeLen, List<PatchRecord> patch) throws IOException { |
361 int last = 0, destIndex = 0; | 372 int last = 0, destIndex = 0; |
362 if (outcomeLen == -1) { | 373 if (outcomeLen == -1) { |
363 outcomeLen = (int) baseRevisionContent.length(); | 374 outcomeLen = baseRevisionContent.length(); |
364 for (PatchRecord pr : patch) { | 375 for (PatchRecord pr : patch) { |
365 outcomeLen += pr.start - last + pr.len; | 376 outcomeLen += pr.start - last + pr.len; |
366 last = pr.end; | 377 last = pr.end; |
367 } | 378 } |
368 outcomeLen -= last; | 379 outcomeLen -= last; |