Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 263:31f67be94e71
RevlogStream - reduce number of object instances, reuse when possible
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 18 Aug 2011 18:06:44 +0200 |
parents | e5776067b3b8 |
children | 6bb5e7ed051a |
comparison
equal
deleted
inserted
replaced
262:3dcd3dd90c77 | 263:31f67be94e71 |
---|---|
21 | 21 |
22 import java.io.File; | 22 import java.io.File; |
23 import java.io.IOException; | 23 import java.io.IOException; |
24 import java.util.ArrayList; | 24 import java.util.ArrayList; |
25 import java.util.List; | 25 import java.util.List; |
26 import java.util.zip.Inflater; | |
26 | 27 |
27 import org.tmatesoft.hg.core.HgBadStateException; | 28 import org.tmatesoft.hg.core.HgBadStateException; |
28 import org.tmatesoft.hg.core.Nodeid; | 29 import org.tmatesoft.hg.core.Nodeid; |
29 import org.tmatesoft.hg.repo.HgRepository; | 30 import org.tmatesoft.hg.repo.HgRepository; |
30 | 31 |
335 private final boolean needData; | 336 private final boolean needData; |
336 private DataAccess daIndex = null, daData = null; | 337 private DataAccess daIndex = null, daData = null; |
337 private Lifecycle.BasicCallback cb = null; | 338 private Lifecycle.BasicCallback cb = null; |
338 private int lastRevisionRead = BAD_REVISION; | 339 private int lastRevisionRead = BAD_REVISION; |
339 private DataAccess lastUserData; | 340 private DataAccess lastUserData; |
341 // next are to track two major bottlenecks - patch application and actual time spent in inspector | |
342 // private long applyTime, inspectorTime; | |
343 | |
340 | 344 |
341 public ReaderN1(boolean needData, Inspector insp) { | 345 public ReaderN1(boolean needData, Inspector insp) { |
342 assert insp != null; | 346 assert insp != null; |
343 this.needData = needData; | 347 this.needData = needData; |
344 inspector = insp; | 348 inspector = insp; |
351 } | 355 } |
352 if (inspector instanceof Lifecycle) { | 356 if (inspector instanceof Lifecycle) { |
353 cb = new Lifecycle.BasicCallback(); | 357 cb = new Lifecycle.BasicCallback(); |
354 ((Lifecycle) inspector).start(totalWork, cb, cb); | 358 ((Lifecycle) inspector).start(totalWork, cb, cb); |
355 } | 359 } |
360 // applyTime = inspectorTime = 0; | |
356 } | 361 } |
357 | 362 |
358 public void finish() { | 363 public void finish() { |
359 if (lastUserData != null) { | 364 if (lastUserData != null) { |
360 lastUserData.done(); | 365 lastUserData.done(); |
365 } | 370 } |
366 daIndex.done(); | 371 daIndex.done(); |
367 if (daData != null) { | 372 if (daData != null) { |
368 daData.done(); | 373 daData.done(); |
369 } | 374 } |
370 } | 375 // System.out.printf("applyTime:%d ms, inspectorTime: %d ms\n", applyTime, inspectorTime); |
371 | 376 } |
377 | |
372 public boolean range(int start, int end) throws IOException { | 378 public boolean range(int start, int end) throws IOException { |
373 byte[] nodeidBuf = new byte[20]; | 379 byte[] nodeidBuf = new byte[20]; |
374 int i; | 380 int i; |
375 boolean extraReadsToBaseRev = false; // to indicate we read revision prior to start. XXX not sure can't do without | 381 boolean extraReadsToBaseRev = false; // to indicate we read revision prior to start. XXX not sure can't do without |
376 // it (i.e. replace with i >= start) | 382 // it (i.e. replace with i >= start) |
392 i = start; | 398 i = start; |
393 } | 399 } |
394 | 400 |
395 daIndex.seek(getIndexOffsetInt(i)); | 401 daIndex.seek(getIndexOffsetInt(i)); |
396 // | 402 // |
403 // reuse some instances | |
397 final ArrayList<PatchRecord> patches = new ArrayList<PatchRecord>(); | 404 final ArrayList<PatchRecord> patches = new ArrayList<PatchRecord>(); |
405 final Inflater inflater = new Inflater(); | |
406 // can share buffer between instances of InflaterDataAccess as I never read any two of them in parallel | |
407 final byte[] inflaterBuffer = new byte[1024]; | |
408 // | |
398 | 409 |
399 for (; i <= end; i++ ) { | 410 for (; i <= end; i++ ) { |
400 if (inline && needData) { | 411 if (inline && needData) { |
401 // inspector reading data (though FilterDataAccess) may have affected index position | 412 // inspector reading data (though FilterDataAccess) may have affected index position |
402 daIndex.seek(getIndexOffsetInt(i)); | 413 daIndex.seek(getIndexOffsetInt(i)); |
430 if (streamDataAccess.isEmpty()) { | 441 if (streamDataAccess.isEmpty()) { |
431 userDataAccess = new DataAccess(); // empty | 442 userDataAccess = new DataAccess(); // empty |
432 } else { | 443 } else { |
433 final byte firstByte = streamDataAccess.readByte(); | 444 final byte firstByte = streamDataAccess.readByte(); |
434 if (firstByte == 0x78 /* 'x' */) { | 445 if (firstByte == 0x78 /* 'x' */) { |
435 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen); | 446 inflater.reset(); |
447 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen, inflater, inflaterBuffer); | |
436 } else if (firstByte == 0x75 /* 'u' */) { | 448 } else if (firstByte == 0x75 /* 'u' */) { |
437 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); | 449 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); |
438 } else { | 450 } else { |
439 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' | 451 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' |
440 // but I don't see reason not to return data as is | 452 // but I don't see reason not to return data as is |
442 } | 454 } |
443 } | 455 } |
444 // XXX | 456 // XXX |
445 if (patchToPrevious) { | 457 if (patchToPrevious) { |
446 // this is a patch | 458 // this is a patch |
459 patches.clear(); // won't hurt to ensure there are no leftovers, even if we already cleaned | |
447 while (!userDataAccess.isEmpty()) { | 460 while (!userDataAccess.isEmpty()) { |
448 PatchRecord pr = PatchRecord.read(userDataAccess); | 461 PatchRecord pr = PatchRecord.read(userDataAccess); |
449 // System.out.printf("PatchRecord:%d %d %d\n", pr.start, pr.end, pr.len); | 462 // System.out.printf("PatchRecord:%d %d %d\n", pr.start, pr.end, pr.len); |
450 patches.add(pr); | 463 patches.add(pr); |
451 } | 464 } |
452 userDataAccess.done(); | 465 userDataAccess.done(); |
453 // | 466 // |
467 // it shall be reset at the end of prev iteration, when it got assigned from userDataAccess | |
468 // however, actual userDataAccess and lastUserData may share Inflater object, which needs to be reset | |
469 // Alternatively, userDataAccess.done() above may be responsible to reset Inflater (if it's InflaterDataAccess) | |
470 lastUserData.reset(); | |
471 // final long startMeasuring = System.currentTimeMillis(); | |
454 byte[] userData = apply(lastUserData, actualLen, patches); | 472 byte[] userData = apply(lastUserData, actualLen, patches); |
455 patches.clear(); | 473 // applyTime += (System.currentTimeMillis() - startMeasuring); |
474 patches.clear(); // do not keep any reference, allow PatchRecord to be gc'd | |
456 userDataAccess = new ByteArrayDataAccess(userData); | 475 userDataAccess = new ByteArrayDataAccess(userData); |
457 } | 476 } |
458 } else { | 477 } else { |
459 if (inline) { | 478 if (inline) { |
460 daIndex.skip(compressedLen); | 479 daIndex.skip(compressedLen); |
461 } | 480 } |
462 } | 481 } |
463 if (!extraReadsToBaseRev || i >= start) { | 482 if (!extraReadsToBaseRev || i >= start) { |
483 // final long startMeasuring = System.currentTimeMillis(); | |
464 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeidBuf, userDataAccess); | 484 inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeidBuf, userDataAccess); |
485 // inspectorTime += (System.currentTimeMillis() - startMeasuring); | |
465 } | 486 } |
466 if (cb != null) { | 487 if (cb != null) { |
467 if (cb.isStopped()) { | 488 if (cb.isStopped()) { |
468 return false; | 489 return false; |
469 } | 490 } |
470 } | 491 } |
471 if (userDataAccess != null) { | 492 if (userDataAccess != null) { |
472 userDataAccess.reset(); | 493 userDataAccess.reset(); // not sure this is necessary here, as lastUserData would get reset anyway before next use. |
473 if (lastUserData != null) { | 494 } |
474 lastUserData.done(); | 495 if (lastUserData != null) { |
475 } | 496 lastUserData.done(); |
476 lastUserData = userDataAccess; | 497 } |
477 } | 498 lastUserData = userDataAccess; |
478 } | 499 } |
479 lastRevisionRead = end; | 500 lastRevisionRead = end; |
480 return true; | 501 return true; |
481 } | 502 } |
482 } | 503 } |
495 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF | 516 // FIXME need to implement patch merge (fold, combine, gather and discard from aforementioned mpatch.[c|py]), also see Revlog and Mercurial PDF |
496 public/*for HgBundle; until moved to better place*/static byte[] apply(DataAccess baseRevisionContent, int outcomeLen, List<PatchRecord> patch) throws IOException { | 517 public/*for HgBundle; until moved to better place*/static byte[] apply(DataAccess baseRevisionContent, int outcomeLen, List<PatchRecord> patch) throws IOException { |
497 int last = 0, destIndex = 0; | 518 int last = 0, destIndex = 0; |
498 if (outcomeLen == -1) { | 519 if (outcomeLen == -1) { |
499 outcomeLen = baseRevisionContent.length(); | 520 outcomeLen = baseRevisionContent.length(); |
500 for (PatchRecord pr : patch) { | 521 for (int i = 0, x = patch.size(); i < x; i++) { |
522 PatchRecord pr = patch.get(i); | |
501 outcomeLen += pr.start - last + pr.len; | 523 outcomeLen += pr.start - last + pr.len; |
502 last = pr.end; | 524 last = pr.end; |
503 } | 525 } |
504 outcomeLen -= last; | 526 outcomeLen -= last; |
505 last = 0; | 527 last = 0; |
506 } | 528 } |
507 byte[] rv = new byte[outcomeLen]; | 529 byte[] rv = new byte[outcomeLen]; |
508 for (PatchRecord pr : patch) { | 530 for (int i = 0, x = patch.size(); i < x; i++) { |
531 PatchRecord pr = patch.get(i); | |
509 baseRevisionContent.seek(last); | 532 baseRevisionContent.seek(last); |
510 baseRevisionContent.readBytes(rv, destIndex, pr.start-last); | 533 baseRevisionContent.readBytes(rv, destIndex, pr.start-last); |
511 destIndex += pr.start - last; | 534 destIndex += pr.start - last; |
512 System.arraycopy(pr.data, 0, rv, destIndex, pr.data.length); | 535 System.arraycopy(pr.data, 0, rv, destIndex, pr.data.length); |
513 destIndex += pr.data.length; | 536 destIndex += pr.data.length; |