1
2
3
4
5
6
7
8
9
10
11
12 package org.eclipse.jgit.internal.storage.pack;
13
14 import static org.eclipse.jgit.lib.Constants.OBJ_OFS_DELTA;
15 import static org.eclipse.jgit.lib.Constants.OBJ_REF_DELTA;
16 import static org.eclipse.jgit.lib.Constants.PACK_SIGNATURE;
17
18 import java.io.IOException;
19 import java.io.OutputStream;
20 import java.security.MessageDigest;
21
22 import org.eclipse.jgit.internal.JGitText;
23 import org.eclipse.jgit.lib.Constants;
24 import org.eclipse.jgit.lib.ProgressMonitor;
25 import org.eclipse.jgit.util.NB;
26
27
28
29
30
31 public final class PackOutputStream extends OutputStream {
32 private static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024;
33
34 private final ProgressMonitor writeMonitor;
35
36 private final OutputStream out;
37
38 private final PackWriter packWriter;
39
40 private final MessageDigest md = Constants.newMessageDigest();
41
42 private long count;
43
44 private final byte[] headerBuffer = new byte[32];
45
46 private final byte[] copyBuffer = new byte[64 << 10];
47
48 private long checkCancelAt;
49
50 private boolean ofsDelta;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public PackOutputStream(final ProgressMonitor writeMonitor,
68 final OutputStream out, final PackWriter pw) {
69 this.writeMonitor = writeMonitor;
70 this.out = out;
71 this.packWriter = pw;
72 this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
73 }
74
75
76 @Override
77 public final void write(int b) throws IOException {
78 count++;
79 out.write(b);
80 md.update((byte) b);
81 }
82
83
84 @Override
85 public final void write(byte[] b, int off, int len)
86 throws IOException {
87 while (0 < len) {
88 final int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK);
89 count += n;
90
91 if (checkCancelAt <= count) {
92 if (writeMonitor.isCancelled()) {
93 throw new IOException(
94 JGitText.get().packingCancelledDuringObjectsWriting);
95 }
96 checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
97 }
98
99 out.write(b, off, n);
100 md.update(b, off, n);
101
102 off += n;
103 len -= n;
104 }
105 }
106
107
108 @Override
109 public void flush() throws IOException {
110 out.flush();
111 }
112
113 final void writeFileHeader(int version, long objectCount)
114 throws IOException {
115 System.arraycopy(PACK_SIGNATURE, 0, headerBuffer, 0, 4);
116 NB.encodeInt32(headerBuffer, 4, version);
117 NB.encodeInt32(headerBuffer, 8, (int) objectCount);
118 write(headerBuffer, 0, 12);
119 ofsDelta = packWriter.isDeltaBaseAsOffset();
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 public final void writeObject(ObjectToPack otp) throws IOException {
138 packWriter.writeObject(this, otp);
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 @SuppressWarnings("ShortCircuitBoolean")
158 public final void writeHeader(ObjectToPack otp, long rawLength)
159 throws IOException {
160 ObjectToPack b = otp.getDeltaBase();
161 if (b != null && (b.isWritten() & ofsDelta)) {
162 int n = objectHeader(rawLength, OBJ_OFS_DELTA, headerBuffer);
163 n = ofsDelta(count - b.getOffset(), headerBuffer, n);
164 write(headerBuffer, 0, n);
165 } else if (otp.isDeltaRepresentation()) {
166 int n = objectHeader(rawLength, OBJ_REF_DELTA, headerBuffer);
167 otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
168 write(headerBuffer, 0, n + 20);
169 } else {
170 int n = objectHeader(rawLength, otp.getType(), headerBuffer);
171 write(headerBuffer, 0, n);
172 }
173 }
174
175 private static final int objectHeader(long len, int type, byte[] buf) {
176 byte b = (byte) ((type << 4) | (len & 0x0F));
177 int n = 0;
178 for (len >>>= 4; len != 0; len >>>= 7) {
179 buf[n++] = (byte) (0x80 | b);
180 b = (byte) (len & 0x7F);
181 }
182 buf[n++] = b;
183 return n;
184 }
185
186 private static final int ofsDelta(long diff, byte[] buf, int p) {
187 p += ofsDeltaVarIntLength(diff);
188 int n = p;
189 buf[--n] = (byte) (diff & 0x7F);
190 while ((diff >>>= 7) != 0)
191 buf[--n] = (byte) (0x80 | (--diff & 0x7F));
192 return p;
193 }
194
195 private static final int ofsDeltaVarIntLength(long v) {
196 int n = 1;
197 for (; (v >>>= 7) != 0; n++)
198 --v;
199 return n;
200 }
201
202
203
204
205
206
207 public final byte[] getCopyBuffer() {
208 return copyBuffer;
209 }
210
211 void endObject() {
212 writeMonitor.update(1);
213 }
214
215
216
217
218
219
220 public final long length() {
221 return count;
222 }
223
224
225 final byte[] getDigest() {
226 return md.digest();
227 }
228 }