Mercurial > jhg
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 } |
