diff 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
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/Revlog.java	Thu Dec 01 05:21:40 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/Revlog.java	Tue Dec 06 12:57:21 2011 +0100
@@ -36,11 +36,13 @@
 import org.tmatesoft.hg.internal.ArrayHelper;
 import org.tmatesoft.hg.internal.DataAccess;
 import org.tmatesoft.hg.internal.Experimental;
+import org.tmatesoft.hg.internal.Preview;
 import org.tmatesoft.hg.internal.RevlogStream;
 import org.tmatesoft.hg.util.Adaptable;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.LogFacility;
 import org.tmatesoft.hg.util.ProgressSupport;
 
 
@@ -178,7 +180,7 @@
 		if (sink == null) {
 			throw new IllegalArgumentException();
 		}
-		ContentPipe insp = new ContentPipe(sink, 0);
+		ContentPipe insp = new ContentPipe(sink, 0, repo.getContext().getLog());
 		insp.checkCancelled();
 		content.iterate(revision, revision, true, insp);
 		insp.checkFailed();
@@ -596,16 +598,19 @@
 	protected static class ContentPipe extends ErrorHandlingInspector implements RevlogStream.Inspector, CancelSupport {
 		private final ByteChannel sink;
 		private final int offset;
+		private final LogFacility logFacility;
 
 		/**
 		 * @param _sink - cannot be <code>null</code>
 		 * @param seekOffset - when positive, orders to pipe bytes to the sink starting from specified offset, not from the first byte available in DataAccess
+		 * @param log optional facility to put warnings/debug messages into, may be null.
 		 */
-		public ContentPipe(ByteChannel _sink, int seekOffset) {
+		public ContentPipe(ByteChannel _sink, int seekOffset, LogFacility log) {
 			assert _sink != null;
 			sink = _sink;
 			setCancelSupport(CancelSupport.Factory.get(_sink));
 			offset = seekOffset;
+			logFacility = log;
 		}
 		
 		protected void prepare(int revisionNumber, DataAccess da) throws HgException, IOException {
@@ -618,16 +623,37 @@
 			try {
 				prepare(revisionNumber, da); // XXX perhaps, prepare shall return DA (sliced, if needed)
 				final ProgressSupport progressSupport = ProgressSupport.Factory.get(sink);
-				ByteBuffer buf = ByteBuffer.allocate(512);
-				progressSupport.start(da.length());
+				ByteBuffer buf = ByteBuffer.allocate(actualLen > 8192 ? 8192 : actualLen);
+				Preview p = getAdapter(sink, Preview.class);
+				if (p != null) {
+					progressSupport.start(2 * da.length());
+					while (!da.isEmpty()) {
+						checkCancelled();
+						da.readBytes(buf);
+						p.preview(buf);
+						buf.clear();
+					}
+					da.reset();
+					prepare(revisionNumber, da);
+					progressSupport.worked(da.length());
+					buf.clear();
+				} else {
+					progressSupport.start(da.length());
+				}
 				while (!da.isEmpty()) {
 					checkCancelled();
 					da.readBytes(buf);
-					buf.flip();
+					buf.flip(); // post: position == 0
 					// XXX I may not rely on returned number of bytes but track change in buf position instead.
-					int consumed = sink.write(buf); 
-					// FIXME in fact, bad sink implementation (that consumes no bytes) would result in endless loop. Need to account for this 
-					buf.compact();
+					
+					int consumed = sink.write(buf);
+					if ((consumed == 0 || consumed != buf.position()) && logFacility != null) {
+						logFacility.warn(getClass(), "Bad data sink when reading revision %d. Reported %d bytes consumed, byt actually read %d", revisionNumber, consumed, buf.position());
+					}
+					if (buf.position() == 0) {
+						throw new HgBadStateException("Bad sink implementation (consumes no bytes) results in endless loop");
+					}
+					buf.compact(); // ensure (a) there's space for new (b) data starts at 0
 					progressSupport.worked(consumed);
 				}
 				progressSupport.done(); // XXX shall specify whether #done() is invoked always or only if completed successfully.