comparison src/org/tmatesoft/hg/repo/Revlog.java @ 157:d5268ca7715b

Merged branch wrap-data-access into default for resource-friendly data access. Updated API to promote that friendliness to clients (channels, not byte[]). More exceptions
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 09 Mar 2011 05:22:17 +0100
parents src/com/tmate/hgkit/ll/Revlog.java@9429c7bd1920 src/com/tmate/hgkit/ll/Revlog.java@3959bffb14e9
children 2c3e96674e2a
comparison
equal deleted inserted replaced
156:643ddec3be36 157:d5268ca7715b
17 package org.tmatesoft.hg.repo; 17 package org.tmatesoft.hg.repo;
18 18
19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; 19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
20 import static org.tmatesoft.hg.repo.HgRepository.TIP; 20 import static org.tmatesoft.hg.repo.HgRepository.TIP;
21 21
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
22 import java.util.Arrays; 24 import java.util.Arrays;
23 import java.util.Collection; 25 import java.util.Collection;
24 import java.util.Collections; 26 import java.util.Collections;
25 import java.util.HashMap; 27 import java.util.HashMap;
26 import java.util.LinkedHashSet; 28 import java.util.LinkedHashSet;
27 import java.util.Map; 29 import java.util.Map;
28 import java.util.Set; 30 import java.util.Set;
29 31
32 import org.tmatesoft.hg.core.HgBadStateException;
33 import org.tmatesoft.hg.core.HgException;
30 import org.tmatesoft.hg.core.Nodeid; 34 import org.tmatesoft.hg.core.Nodeid;
35 import org.tmatesoft.hg.internal.DataAccess;
31 import org.tmatesoft.hg.internal.RevlogStream; 36 import org.tmatesoft.hg.internal.RevlogStream;
37 import org.tmatesoft.hg.util.ByteChannel;
38 import org.tmatesoft.hg.util.CancelSupport;
39 import org.tmatesoft.hg.util.CancelledException;
40 import org.tmatesoft.hg.util.ProgressSupport;
32 41
33 42
34 /** 43 /**
35 * 44 * Base class for all Mercurial entities that are serialized in a so called revlog format (changelog, manifest, data files).
45 *
46 * Implementation note:
47 * Hides actual actual revlog stream implementation and its access methods (i.e. RevlogStream.Inspector), iow shall not expose anything internal
48 * in public methods.
49 *
36 * @author Artem Tikhomirov 50 * @author Artem Tikhomirov
37 * @author TMate Software Ltd. 51 * @author TMate Software Ltd.
38 */ 52 */
39 abstract class Revlog { 53 abstract class Revlog {
40 54
98 112
99 /** 113 /**
100 * Access to revision data as is (decompressed, but otherwise unprocessed, i.e. not parsed for e.g. changeset or manifest entries) 114 * Access to revision data as is (decompressed, but otherwise unprocessed, i.e. not parsed for e.g. changeset or manifest entries)
101 * @param nodeid 115 * @param nodeid
102 */ 116 */
103 public byte[] content(Nodeid nodeid) { 117 protected void rawContent(Nodeid nodeid, ByteChannel sink) throws HgException, IOException, CancelledException {
104 return content(getLocalRevision(nodeid)); 118 rawContent(getLocalRevision(nodeid), sink);
105 } 119 }
106 120
107 /** 121 /**
108 * @param revision - repo-local index of this file change (not a changelog revision number!) 122 * @param revision - repo-local index of this file change (not a changelog revision number!)
109 */ 123 */
110 public byte[] content(int revision) { 124 protected void rawContent(int revision, ByteChannel sink) throws HgException, IOException, CancelledException {
111 final byte[][] dataPtr = new byte[1][]; 125 if (sink == null) {
112 RevlogStream.Inspector insp = new RevlogStream.Inspector() { 126 throw new IllegalArgumentException();
113 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { 127 }
114 dataPtr[0] = data; 128 ContentPipe insp = new ContentPipe(sink, 0);
115 } 129 insp.checkCancelled();
116 };
117 content.iterate(revision, revision, true, insp); 130 content.iterate(revision, revision, true, insp);
118 return dataPtr[0]; 131 insp.checkFailed();
119 } 132 }
120 133
121 /** 134 /**
122 * XXX perhaps, return value Nodeid[2] and boolean needNodeids is better (and higher level) API for this query? 135 * XXX perhaps, return value Nodeid[2] and boolean needNodeids is better (and higher level) API for this query?
123 * 136 *
143 class ParentCollector implements RevlogStream.Inspector { 156 class ParentCollector implements RevlogStream.Inspector {
144 public int p1 = -1; 157 public int p1 = -1;
145 public int p2 = -1; 158 public int p2 = -1;
146 public byte[] nodeid; 159 public byte[] nodeid;
147 160
148 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { 161 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
149 p1 = parent1Revision; 162 p1 = parent1Revision;
150 p2 = parent2Revision; 163 p2 = parent2Revision;
151 this.nodeid = new byte[20]; 164 this.nodeid = new byte[20];
152 // nodeid arg now comes in 32 byte from (as in file format description), however upper 12 bytes are zeros. 165 // nodeid arg now comes in 32 byte from (as in file format description), however upper 12 bytes are zeros.
153 System.arraycopy(nodeid, nodeid.length > 20 ? nodeid.length - 20 : 0, this.nodeid, 0, 20); 166 System.arraycopy(nodeid, nodeid.length > 20 ? nodeid.length - 20 : 0, this.nodeid, 0, 20);
201 allNodes = new LinkedHashSet<Nodeid>(); 214 allNodes = new LinkedHashSet<Nodeid>();
202 215
203 RevlogStream.Inspector insp = new RevlogStream.Inspector() { 216 RevlogStream.Inspector insp = new RevlogStream.Inspector() {
204 final Nodeid[] sequentialRevisionNodeids = new Nodeid[revisionCount]; 217 final Nodeid[] sequentialRevisionNodeids = new Nodeid[revisionCount];
205 int ix = 0; 218 int ix = 0;
206 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { 219 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
207 if (ix != revisionNumber) { 220 if (ix != revisionNumber) {
208 // XXX temp code, just to make sure I understand what's going on here 221 // XXX temp code, just to make sure I understand what's going on here
209 throw new IllegalStateException(); 222 throw new IllegalStateException();
210 } 223 }
211 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { 224 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) {
265 } 278 }
266 } 279 }
267 return modified; 280 return modified;
268 } 281 }
269 } 282 }
283
284 protected static class ContentPipe implements RevlogStream.Inspector, CancelSupport {
285 private final ByteChannel sink;
286 private final CancelSupport cancelSupport;
287 private Exception failure;
288 private final int offset;
289
290 /**
291 * @param _sink - cannot be <code>null</code>
292 * @param seekOffset - when positive, orders to pipe bytes to the sink starting from specified offset, not from the first byte available in DataAccess
293 */
294 public ContentPipe(ByteChannel _sink, int seekOffset) {
295 assert _sink != null;
296 sink = _sink;
297 cancelSupport = CancelSupport.Factory.get(_sink);
298 offset = seekOffset;
299 }
300
301 protected void prepare(int revisionNumber, DataAccess da) throws HgException, IOException {
302 if (offset > 0) { // save few useless reset/rewind operations
303 da.seek(offset);
304 }
305 }
306
307 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
308 try {
309 prepare(revisionNumber, da); // XXX perhaps, prepare shall return DA (sliced, if needed)
310 final ProgressSupport progressSupport = ProgressSupport.Factory.get(sink);
311 ByteBuffer buf = ByteBuffer.allocate(512);
312 progressSupport.start(da.length());
313 while (!da.isEmpty()) {
314 cancelSupport.checkCancelled();
315 da.readBytes(buf);
316 buf.flip();
317 // XXX I may not rely on returned number of bytes but track change in buf position instead.
318 int consumed = sink.write(buf);
319 // FIXME in fact, bad sink implementation (that consumes no bytes) would result in endless loop. Need to account for this
320 buf.compact();
321 progressSupport.worked(consumed);
322 }
323 progressSupport.done(); // XXX shall specify whether #done() is invoked always or only if completed successfully.
324 } catch (IOException ex) {
325 recordFailure(ex);
326 } catch (CancelledException ex) {
327 recordFailure(ex);
328 } catch (HgException ex) {
329 recordFailure(ex);
330 }
331 }
332
333 public void checkCancelled() throws CancelledException {
334 cancelSupport.checkCancelled();
335 }
336
337 protected void recordFailure(Exception ex) {
338 assert failure == null;
339 failure = ex;
340 }
341
342 public void checkFailed() throws HgException, IOException, CancelledException {
343 if (failure == null) {
344 return;
345 }
346 if (failure instanceof IOException) {
347 throw (IOException) failure;
348 }
349 if (failure instanceof CancelledException) {
350 throw (CancelledException) failure;
351 }
352 if (failure instanceof HgException) {
353 throw (HgException) failure;
354 }
355 throw new HgBadStateException(failure);
356 }
357 }
270 } 358 }