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