Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/Revlog.java @ 448:2e402c12ebc6 smartgit3
Issue 31: Revlog#walk() fails with AIOOBE when start > 0
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 06 Jun 2012 21:23:57 +0200 |
| parents | d0e5dc3cae6e |
| children | 7bcfbc255f48 |
comparison
equal
deleted
inserted
replaced
| 447:056f724bdc21 | 448:2e402c12ebc6 |
|---|---|
| 34 import org.tmatesoft.hg.core.HgInvalidRevisionException; | 34 import org.tmatesoft.hg.core.HgInvalidRevisionException; |
| 35 import org.tmatesoft.hg.core.Nodeid; | 35 import org.tmatesoft.hg.core.Nodeid; |
| 36 import org.tmatesoft.hg.internal.ArrayHelper; | 36 import org.tmatesoft.hg.internal.ArrayHelper; |
| 37 import org.tmatesoft.hg.internal.DataAccess; | 37 import org.tmatesoft.hg.internal.DataAccess; |
| 38 import org.tmatesoft.hg.internal.Experimental; | 38 import org.tmatesoft.hg.internal.Experimental; |
| 39 import org.tmatesoft.hg.internal.IntMap; | |
| 39 import org.tmatesoft.hg.internal.Preview; | 40 import org.tmatesoft.hg.internal.Preview; |
| 40 import org.tmatesoft.hg.internal.RevlogStream; | 41 import org.tmatesoft.hg.internal.RevlogStream; |
| 41 import org.tmatesoft.hg.util.Adaptable; | 42 import org.tmatesoft.hg.util.Adaptable; |
| 42 import org.tmatesoft.hg.util.ByteChannel; | 43 import org.tmatesoft.hg.util.ByteChannel; |
| 43 import org.tmatesoft.hg.util.CancelSupport; | 44 import org.tmatesoft.hg.util.CancelSupport; |
| 257 } | 258 } |
| 258 | 259 |
| 259 @Experimental | 260 @Experimental |
| 260 public final void walk(int start, int end, final Revlog.Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | 261 public final void walk(int start, int end, final Revlog.Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { |
| 261 int lastRev = getLastRevision(); | 262 int lastRev = getLastRevision(); |
| 262 if (start == TIP) { | 263 final int _start = start == TIP ? lastRev : start; |
| 263 start = lastRev; | |
| 264 } | |
| 265 if (end == TIP) { | 264 if (end == TIP) { |
| 266 end = lastRev; | 265 end = lastRev; |
| 267 } | 266 } |
| 268 final RevisionInspector revisionInsp = Adaptable.Factory.getAdapter(inspector, RevisionInspector.class, null); | 267 final RevisionInspector revisionInsp = Adaptable.Factory.getAdapter(inspector, RevisionInspector.class, null); |
| 269 final ParentInspector parentInsp = Adaptable.Factory.getAdapter(inspector, ParentInspector.class, null); | 268 final ParentInspector parentInsp = Adaptable.Factory.getAdapter(inspector, ParentInspector.class, null); |
| 270 final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - start + 1]; | 269 final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - _start + 1]; |
| 271 if (parentInsp != null && start != 0) { | 270 // next are to build set of parent indexes that are not part of the range iteration |
| 272 // e.g. start == 6, end == 7 and parentOf(start) == 5. allRevisions.length == 2, allRevisions[parentOf(start)] => AIOOBE | 271 // i.e. those parents we need to read separately. See Issue 31 for details. |
| 273 throw new IllegalStateException("There's a defect in the code that doesn't allow walks other than from the very beginning"); | 272 final int[] firstParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; |
| 274 // TestAuxUtilities#testRevlogInspectors | 273 final int[] secondParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; |
| 275 } | 274 final IntMap<Nodeid> missingParents = parentInsp == null || _start == 0 ? null : new IntMap<Nodeid>(16); |
| 276 | 275 |
| 277 content.iterate(start, end, false, new RevlogStream.Inspector() { | 276 content.iterate(_start, end, false, new RevlogStream.Inspector() { |
| 277 private int i = 0; | |
| 278 | 278 |
| 279 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 279 public void next(int revisionIndex, int actualLen, int baseRevIndex, int linkRevIndex, int parent1RevIndex, int parent2RevIndex, byte[] nodeid, DataAccess data) { |
| 280 Nodeid nid = Nodeid.fromBinary(nodeid, 0); | 280 Nodeid nid = Nodeid.fromBinary(nodeid, 0); |
| 281 if (revisionInsp != null) { | 281 if (revisionInsp != null) { |
| 282 revisionInsp.next(revisionNumber, nid, linkRevision); | 282 revisionInsp.next(revisionIndex, nid, linkRevIndex); |
| 283 } | 283 } |
| 284 if (parentInsp != null) { | 284 if (parentInsp != null) { |
| 285 Nodeid p1 = parent1Revision == -1 ? Nodeid.NULL : allRevisions[parent1Revision]; | 285 allRevisions[i] = nid; |
| 286 Nodeid p2 = parent2Revision == -1 ? Nodeid.NULL : allRevisions[parent2Revision]; | 286 if (_start > 0) { |
| 287 allRevisions[revisionNumber] = nid; | 287 firstParentIndexes[i] = parent1RevIndex; |
| 288 parentInsp.next(revisionNumber, nid, parent1Revision, parent2Revision, p1, p2); | 288 secondParentIndexes[i] = parent2RevIndex; |
| 289 if (parent1RevIndex < _start && parent1RevIndex >= 0) { | |
| 290 missingParents.put(parent1RevIndex, null); | |
| 291 } | |
| 292 if (parent2RevIndex < _start && parent2RevIndex >= 0) { | |
| 293 missingParents.put(parent2RevIndex, null); | |
| 294 } | |
| 295 } else { | |
| 296 Nodeid p1 = parent1RevIndex == -1 ? Nodeid.NULL : allRevisions[parent1RevIndex]; | |
| 297 Nodeid p2 = parent2RevIndex == -1 ? Nodeid.NULL : allRevisions[parent2RevIndex]; | |
| 298 parentInsp.next(revisionIndex, allRevisions[i], parent1RevIndex, parent2RevIndex, p1, p2); | |
| 299 } | |
| 300 i++; | |
| 289 } | 301 } |
| 290 } | 302 } |
| 291 }); | 303 }); |
| 304 if (parentInsp != null && _start > 0) { | |
| 305 assert missingParents.size() > 0; // in fact, more relaxed than assert. rather 'assume' | |
| 306 // TODO int[] IntMap#keys() or even sort of iterator that can modify values | |
| 307 for (int k = missingParents.firstKey(), l = missingParents.lastKey(); k <= l; k++) { | |
| 308 if (missingParents.containsKey(k)) { | |
| 309 Nodeid nid = getRepo().getChangelog().getRevision(k); | |
| 310 missingParents.put(k, nid); | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 for (int i = 0, revNum = _start; i < allRevisions.length; i++, revNum++) { | |
| 315 int riP1 = firstParentIndexes[i]; | |
| 316 int riP2 = secondParentIndexes[i]; | |
| 317 Nodeid p1, p2; | |
| 318 p1 = p2 = Nodeid.NULL; | |
| 319 if (riP1 >= _start) { | |
| 320 // p1 of revNum's revision is out of iterated range | |
| 321 // (don't check for riP1<end as I assume parents come prior to children in the changelog) | |
| 322 p1 = allRevisions[riP1 - start]; | |
| 323 } else if (riP1 != -1) { | |
| 324 assert riP1 >=0 && riP1 < _start; | |
| 325 p1 = missingParents.get(riP1); | |
| 326 } | |
| 327 // same for Pp2 | |
| 328 if (riP2 >= _start) { | |
| 329 p2 = allRevisions[riP2 - start]; | |
| 330 } else if (riP2 != -1) { | |
| 331 assert riP2 >= 0 && riP2 < _start; | |
| 332 p2 = missingParents.get(riP2); | |
| 333 } | |
| 334 parentInsp.next(revNum, allRevisions[i], riP1, riP2, p1, p2); | |
| 335 } | |
| 336 } | |
| 292 } | 337 } |
| 293 | 338 |
| 294 /** | 339 /** |
| 295 * MARKER | 340 * MARKER |
| 296 */ | 341 */ |
