2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50
namespace NGit.Storage.Pack
52
/// <summary>Inflates a delta in an incremental way.</summary>
54
/// Inflates a delta in an incremental way.
56
/// Implementations must provide a means to access a stream for the base object.
57
/// This stream may be accessed multiple times, in order to randomly position it
58
/// to match the copy instructions. A
59
/// <code>DeltaStream</code>
60
/// performs an efficient
61
/// skip by only moving through the delta stream, making restarts of stacked
62
/// deltas reasonably efficient.
64
public abstract class DeltaStream : InputStream
66
private const int CMD_COPY = 0;
68
private const int CMD_INSERT = 1;
70
private const int CMD_EOF = 2;
72
private readonly InputStream deltaStream;
74
private long baseSize;
76
private long resultSize;
78
private readonly byte[] cmdbuf = new byte[512];
84
/// <summary>Stream to read from the base object.</summary>
85
/// <remarks>Stream to read from the base object.</remarks>
86
private InputStream baseStream;
89
/// Current position within
90
/// <see cref="baseStream">baseStream</see>
93
private long baseOffset;
99
/// <code>curcmd == CMD_COPY</code>
100
/// , position the base has to be at.
102
private long copyOffset;
104
/// <summary>Total number of bytes in this current command.</summary>
105
/// <remarks>Total number of bytes in this current command.</remarks>
106
private int copySize;
108
/// <summary>Construct a delta application stream, reading instructions.</summary>
109
/// <remarks>Construct a delta application stream, reading instructions.</remarks>
110
/// <param name="deltaStream">the stream to read delta instructions from.</param>
111
/// <exception cref="System.IO.IOException">
112
/// the delta instruction stream cannot be read, or is
113
/// inconsistent with the the base object information.
115
public DeltaStream(InputStream deltaStream)
117
this.deltaStream = deltaStream;
118
if (!Fill(cmdbuf.Length))
120
throw new EOFException();
122
// Length of the base object.
128
c = cmdbuf[cmdptr++] & unchecked((int)(0xff));
129
baseSize |= (c & unchecked((int)(0x7f))) << shift;
132
while ((c & unchecked((int)(0x80))) != 0);
133
// Length of the resulting object.
138
c = cmdbuf[cmdptr++] & unchecked((int)(0xff));
139
resultSize |= (c & unchecked((int)(0x7f))) << shift;
142
while ((c & unchecked((int)(0x80))) != 0);
146
/// <summary>Open the base stream.</summary>
148
/// Open the base stream.
151
/// <code>DeltaStream</code>
152
/// may close and reopen the base stream multiple
153
/// times if copy instructions use offsets out of order. This can occur if a
154
/// large block in the file was moved from near the top, to near the bottom.
155
/// In such cases the reopened stream is skipped to the target offset, so
156
/// <code>skip(long)</code>
157
/// should be as efficient as possible.
160
/// stream to read from the base object. This stream should not be
161
/// buffered (or should be only minimally buffered), and does not
162
/// need to support mark/reset.
164
/// <exception cref="System.IO.IOException">the base object cannot be opened for reading.
166
protected internal abstract InputStream OpenBase();
168
/// <returns>length of the base object, in bytes.</returns>
169
/// <exception cref="System.IO.IOException">the length of the base cannot be determined.
171
protected internal abstract long GetBaseSize();
173
/// <returns>total size of this stream, in bytes.</returns>
174
public virtual long GetSize()
179
/// <exception cref="System.IO.IOException"></exception>
180
public override int Read()
182
byte[] buf = new byte[1];
183
int n = Read(buf, 0, 1);
184
return n == 1 ? buf[0] & unchecked((int)(0xff)) : -1;
187
/// <exception cref="System.IO.IOException"></exception>
188
public override void Close()
191
if (baseStream != null)
197
/// <exception cref="System.IO.IOException"></exception>
198
public override long Skip(long len)
203
long n = Math.Min(len, copySize);
225
throw new CorruptObjectException(JGitText.Get().unsupportedCommand0);
239
/// <exception cref="System.IO.IOException"></exception>
240
public override int Read(byte[] buf, int off, int len)
245
int n = Math.Min(len, copySize);
251
n = baseStream.Read(buf, off, n);
254
throw new CorruptObjectException(JGitText.Get().baseLengthIncorrect);
257
baseOffset = copyOffset;
263
System.Array.Copy(cmdbuf, cmdptr, buf, off, n);
270
return 0 < act ? act : -1;
275
throw new CorruptObjectException(JGitText.Get().unsupportedCommand0);
290
/// <exception cref="System.IO.IOException"></exception>
291
private bool Fill(int need)
305
if (cmdbuf.Length - cmdptr < need)
307
// There isn't room for the entire worst-case copy command,
308
// so shift the array down to make sure we can use the entire
309
// command without having it span across the end of the array.
311
System.Array.Copy(cmdbuf, cmdptr, cmdbuf, 0, n);
318
n = deltaStream.Read(cmdbuf, cmdcnt, cmdbuf.Length - cmdcnt);
325
while (cmdcnt < cmdbuf.Length);
329
/// <exception cref="System.IO.IOException"></exception>
336
int cmd = cmdbuf[cmdptr++] & unchecked((int)(0xff));
337
if ((cmd & unchecked((int)(0x80))) != 0)
339
// Determine the segment of the base which should
340
// be copied into the output. The segment is given
341
// as an offset and a length.
344
if ((cmd & unchecked((int)(0x01))) != 0)
346
copyOffset = cmdbuf[cmdptr++] & unchecked((int)(0xff));
348
if ((cmd & unchecked((int)(0x02))) != 0)
350
copyOffset |= (cmdbuf[cmdptr++] & unchecked((int)(0xff))) << 8;
352
if ((cmd & unchecked((int)(0x04))) != 0)
354
copyOffset |= (cmdbuf[cmdptr++] & unchecked((int)(0xff))) << 16;
356
if ((cmd & unchecked((int)(0x08))) != 0)
358
copyOffset |= (cmdbuf[cmdptr++] & unchecked((int)(0xff))) << 24;
361
if ((cmd & unchecked((int)(0x10))) != 0)
363
copySize = cmdbuf[cmdptr++] & unchecked((int)(0xff));
365
if ((cmd & unchecked((int)(0x20))) != 0)
367
copySize |= (cmdbuf[cmdptr++] & unchecked((int)(0xff))) << 8;
369
if ((cmd & unchecked((int)(0x40))) != 0)
371
copySize |= (cmdbuf[cmdptr++] & unchecked((int)(0xff))) << 16;
375
copySize = unchecked((int)(0x10000));
383
// Anything else the data is literal within the delta
384
// itself. Page the entire thing into the cmdbuf, if
385
// its not already there.
393
// cmd == 0 has been reserved for future encoding but
394
// for now its not acceptable.
396
throw new CorruptObjectException(JGitText.Get().unsupportedCommand0);
403
return cmdcnt - cmdptr;
406
/// <exception cref="System.IO.IOException"></exception>
407
private void SeekBase()
409
if (baseStream == null)
411
baseStream = OpenBase();
412
if (GetBaseSize() != baseSize)
414
throw new CorruptObjectException(JGitText.Get().baseLengthIncorrect);
416
IOUtil.SkipFully(baseStream, copyOffset);
417
baseOffset = copyOffset;
421
if (baseOffset < copyOffset)
423
IOUtil.SkipFully(baseStream, copyOffset - baseOffset);
424
baseOffset = copyOffset;
428
if (baseOffset > copyOffset)
431
baseStream = OpenBase();
432
IOUtil.SkipFully(baseStream, copyOffset);
433
baseOffset = copyOffset;