comparison src/org/tmatesoft/hg/repo/Revlog.java @ 355:f2c11fe7f3e9

Newline filter shall respect whole stream when deciding whether to process line terminators, hence added stream preview functionality
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 06 Dec 2011 12:57:21 +0100
parents 5f9073eabf06
children 91d75e1bac9f
comparison
equal deleted inserted replaced
354:5f9073eabf06 355:f2c11fe7f3e9
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.Preview;
39 import org.tmatesoft.hg.internal.RevlogStream; 40 import org.tmatesoft.hg.internal.RevlogStream;
40 import org.tmatesoft.hg.util.Adaptable; 41 import org.tmatesoft.hg.util.Adaptable;
41 import org.tmatesoft.hg.util.ByteChannel; 42 import org.tmatesoft.hg.util.ByteChannel;
42 import org.tmatesoft.hg.util.CancelSupport; 43 import org.tmatesoft.hg.util.CancelSupport;
43 import org.tmatesoft.hg.util.CancelledException; 44 import org.tmatesoft.hg.util.CancelledException;
45 import org.tmatesoft.hg.util.LogFacility;
44 import org.tmatesoft.hg.util.ProgressSupport; 46 import org.tmatesoft.hg.util.ProgressSupport;
45 47
46 48
47 /** 49 /**
48 * Base class for all Mercurial entities that are serialized in a so called revlog format (changelog, manifest, data files). 50 * Base class for all Mercurial entities that are serialized in a so called revlog format (changelog, manifest, data files).
176 */ 178 */
177 protected void rawContent(int revision, ByteChannel sink) throws HgException, IOException, CancelledException, HgInvalidRevisionException { 179 protected void rawContent(int revision, ByteChannel sink) throws HgException, IOException, CancelledException, HgInvalidRevisionException {
178 if (sink == null) { 180 if (sink == null) {
179 throw new IllegalArgumentException(); 181 throw new IllegalArgumentException();
180 } 182 }
181 ContentPipe insp = new ContentPipe(sink, 0); 183 ContentPipe insp = new ContentPipe(sink, 0, repo.getContext().getLog());
182 insp.checkCancelled(); 184 insp.checkCancelled();
183 content.iterate(revision, revision, true, insp); 185 content.iterate(revision, revision, true, insp);
184 insp.checkFailed(); 186 insp.checkFailed();
185 } 187 }
186 188
594 } 596 }
595 597
596 protected static class ContentPipe extends ErrorHandlingInspector implements RevlogStream.Inspector, CancelSupport { 598 protected static class ContentPipe extends ErrorHandlingInspector implements RevlogStream.Inspector, CancelSupport {
597 private final ByteChannel sink; 599 private final ByteChannel sink;
598 private final int offset; 600 private final int offset;
601 private final LogFacility logFacility;
599 602
600 /** 603 /**
601 * @param _sink - cannot be <code>null</code> 604 * @param _sink - cannot be <code>null</code>
602 * @param seekOffset - when positive, orders to pipe bytes to the sink starting from specified offset, not from the first byte available in DataAccess 605 * @param seekOffset - when positive, orders to pipe bytes to the sink starting from specified offset, not from the first byte available in DataAccess
606 * @param log optional facility to put warnings/debug messages into, may be null.
603 */ 607 */
604 public ContentPipe(ByteChannel _sink, int seekOffset) { 608 public ContentPipe(ByteChannel _sink, int seekOffset, LogFacility log) {
605 assert _sink != null; 609 assert _sink != null;
606 sink = _sink; 610 sink = _sink;
607 setCancelSupport(CancelSupport.Factory.get(_sink)); 611 setCancelSupport(CancelSupport.Factory.get(_sink));
608 offset = seekOffset; 612 offset = seekOffset;
613 logFacility = log;
609 } 614 }
610 615
611 protected void prepare(int revisionNumber, DataAccess da) throws HgException, IOException { 616 protected void prepare(int revisionNumber, DataAccess da) throws HgException, IOException {
612 if (offset > 0) { // save few useless reset/rewind operations 617 if (offset > 0) { // save few useless reset/rewind operations
613 da.seek(offset); 618 da.seek(offset);
616 621
617 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { 622 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
618 try { 623 try {
619 prepare(revisionNumber, da); // XXX perhaps, prepare shall return DA (sliced, if needed) 624 prepare(revisionNumber, da); // XXX perhaps, prepare shall return DA (sliced, if needed)
620 final ProgressSupport progressSupport = ProgressSupport.Factory.get(sink); 625 final ProgressSupport progressSupport = ProgressSupport.Factory.get(sink);
621 ByteBuffer buf = ByteBuffer.allocate(512); 626 ByteBuffer buf = ByteBuffer.allocate(actualLen > 8192 ? 8192 : actualLen);
622 progressSupport.start(da.length()); 627 Preview p = getAdapter(sink, Preview.class);
628 if (p != null) {
629 progressSupport.start(2 * da.length());
630 while (!da.isEmpty()) {
631 checkCancelled();
632 da.readBytes(buf);
633 p.preview(buf);
634 buf.clear();
635 }
636 da.reset();
637 prepare(revisionNumber, da);
638 progressSupport.worked(da.length());
639 buf.clear();
640 } else {
641 progressSupport.start(da.length());
642 }
623 while (!da.isEmpty()) { 643 while (!da.isEmpty()) {
624 checkCancelled(); 644 checkCancelled();
625 da.readBytes(buf); 645 da.readBytes(buf);
626 buf.flip(); 646 buf.flip(); // post: position == 0
627 // XXX I may not rely on returned number of bytes but track change in buf position instead. 647 // XXX I may not rely on returned number of bytes but track change in buf position instead.
628 int consumed = sink.write(buf); 648
629 // FIXME in fact, bad sink implementation (that consumes no bytes) would result in endless loop. Need to account for this 649 int consumed = sink.write(buf);
630 buf.compact(); 650 if ((consumed == 0 || consumed != buf.position()) && logFacility != null) {
651 logFacility.warn(getClass(), "Bad data sink when reading revision %d. Reported %d bytes consumed, byt actually read %d", revisionNumber, consumed, buf.position());
652 }
653 if (buf.position() == 0) {
654 throw new HgBadStateException("Bad sink implementation (consumes no bytes) results in endless loop");
655 }
656 buf.compact(); // ensure (a) there's space for new (b) data starts at 0
631 progressSupport.worked(consumed); 657 progressSupport.worked(consumed);
632 } 658 }
633 progressSupport.done(); // XXX shall specify whether #done() is invoked always or only if completed successfully. 659 progressSupport.done(); // XXX shall specify whether #done() is invoked always or only if completed successfully.
634 } catch (IOException ex) { 660 } catch (IOException ex) {
635 recordFailure(ex); 661 recordFailure(ex);