1 /*
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * Copyright (C) 2009, Google Inc.
5 * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Distribution License v. 1.0 which is available at
9 * https://www.eclipse.org/org/documents/edl-v10.php.
10 *
11 * SPDX-License-Identifier: BSD-3-Clause
12 */
13
14 package org.eclipse.jgit.lib;
15
16 import java.io.ByteArrayInputStream;
17 import java.io.EOFException;
18 import java.io.IOException;
19 import java.io.InputStream;
20
21 import org.eclipse.jgit.internal.JGitText;
22 import org.eclipse.jgit.transport.PackParser;
23 import org.eclipse.jgit.util.sha1.SHA1;
24
25 /**
26 * Inserts objects into an existing {@code ObjectDatabase}.
27 * <p>
28 * An inserter is not thread-safe. Individual threads should each obtain their
29 * own unique inserter instance, or must arrange for locking at a higher level
30 * to ensure the inserter is in use by no more than one thread at a time.
31 * <p>
32 * Objects written by an inserter may not be immediately visible for reading
33 * after the insert method completes. Callers must invoke either
34 * {@link #close()} or {@link #flush()} prior to updating references or
35 * otherwise making the returned ObjectIds visible to other code.
36 */
37 public abstract class ObjectInserter implements AutoCloseable {
38 /** An inserter that can be used for formatting and id generation only. */
39 public static class Formatter extends ObjectInserter {
40 @Override
41 public ObjectId insert(int objectType, long length, InputStream in)
42 throws IOException {
43 throw new UnsupportedOperationException();
44 }
45
46 @Override
47 public PackParser newPackParser(InputStream in) throws IOException {
48 throw new UnsupportedOperationException();
49 }
50
51 @Override
52 public ObjectReader newReader() {
53 throw new UnsupportedOperationException();
54 }
55
56 @Override
57 public void flush() throws IOException {
58 // Do nothing.
59 }
60
61 @Override
62 public void close() {
63 // Do nothing.
64 }
65 }
66
67 /** Wraps a delegate ObjectInserter. */
68 public abstract static class Filter extends ObjectInserter {
69 /** @return delegate ObjectInserter to handle all processing. */
70 protected abstract ObjectInserter delegate();
71
72 @Override
73 protected byte[] buffer() {
74 return delegate().buffer();
75 }
76
77 @Override
78 public ObjectId idFor(int type, byte[] data) {
79 return delegate().idFor(type, data);
80 }
81
82 @Override
83 public ObjectId idFor(int type, byte[] data, int off, int len) {
84 return delegate().idFor(type, data, off, len);
85 }
86
87 @Override
88 public ObjectId idFor(int objectType, long length, InputStream in)
89 throws IOException {
90 return delegate().idFor(objectType, length, in);
91 }
92
93 @Override
94 public ObjectId idFor(TreeFormatter formatter) {
95 return delegate().idFor(formatter);
96 }
97
98 @Override
99 public ObjectId insert(int type, byte[] data) throws IOException {
100 return delegate().insert(type, data);
101 }
102
103 @Override
104 public ObjectId insert(int type, byte[] data, int off, int len)
105 throws IOException {
106 return delegate().insert(type, data, off, len);
107 }
108
109 @Override
110 public ObjectId insert(int objectType, long length, InputStream in)
111 throws IOException {
112 return delegate().insert(objectType, length, in);
113 }
114
115 @Override
116 public PackParser newPackParser(InputStream in) throws IOException {
117 return delegate().newPackParser(in);
118 }
119
120 @Override
121 public ObjectReader newReader() {
122 final ObjectReader dr = delegate().newReader();
123 return new ObjectReader.Filter() {
124 @Override
125 protected ObjectReader delegate() {
126 return dr;
127 }
128
129 @Override
130 public ObjectInserter getCreatedFromInserter() {
131 return ObjectInserter.Filter.this;
132 }
133 };
134 }
135
136 @Override
137 public void flush() throws IOException {
138 delegate().flush();
139 }
140
141 @Override
142 public void close() {
143 delegate().close();
144 }
145 }
146
147 private final SHA1 hasher = SHA1.newInstance();
148
149 /** Temporary working buffer for streaming data through. */
150 private byte[] tempBuffer;
151
152 /**
153 * Create a new inserter for a database.
154 */
155 protected ObjectInserter() {
156 }
157
158 /**
159 * Obtain a temporary buffer for use by the ObjectInserter or its subclass.
160 * <p>
161 * This buffer is supplied by the ObjectInserter base class to itself and
162 * its subclasses for the purposes of pulling data from a supplied
163 * InputStream, passing it through a Deflater, or formatting the canonical
164 * format of a small object like a small tree or commit.
165 * <p>
166 * <strong>This buffer IS NOT for translation such as auto-CRLF or content
167 * filtering and must not be used for such purposes.</strong>
168 * <p>
169 * The returned buffer is small, around a few KiBs, and the size may change
170 * between versions of JGit. Callers using this buffer must always check the
171 * length of the returned array to ascertain how much space was provided.
172 * <p>
173 * There is a single buffer for each ObjectInserter, repeated calls to this
174 * method will (usually) always return the same buffer. If the caller needs
175 * more than one buffer, or needs a buffer of a larger size, it must manage
176 * that buffer on its own.
177 * <p>
178 * The buffer is usually on first demand for a buffer.
179 *
180 * @return a temporary byte array for use by the caller.
181 */
182 protected byte[] buffer() {
183 byte[] b = tempBuffer;
184 if (b == null)
185 tempBuffer = b = new byte[8192];
186 return b;
187 }
188
189 /**
190 * Compute digest to help compute an ObjectId
191 *
192 * @return digest to help compute an ObjectId
193 * @since 4.7
194 */
195 protected SHA1 digest() {
196 return hasher.reset();
197 }
198
199 /**
200 * Compute the name of an object, without inserting it.
201 *
202 * @param type
203 * type code of the object to store.
204 * @param data
205 * complete content of the object.
206 * @return the name of the object.
207 */
208 public ObjectId idFor(int type, byte[] data) {
209 return idFor(type, data, 0, data.length);
210 }
211
212 /**
213 * Compute the name of an object, without inserting it.
214 *
215 * @param type
216 * type code of the object to store.
217 * @param data
218 * complete content of the object.
219 * @param off
220 * first position within {@code data}.
221 * @param len
222 * number of bytes to copy from {@code data}.
223 * @return the name of the object.
224 */
225 public ObjectId idFor(int type, byte[] data, int off, int len) {
226 SHA1 md = SHA1.newInstance();
227 md.update(Constants.encodedTypeString(type));
228 md.update((byte) ' ');
229 md.update(Constants.encodeASCII(len));
230 md.update((byte) 0);
231 md.update(data, off, len);
232 return md.toObjectId();
233 }
234
235 /**
236 * Compute the name of an object, without inserting it.
237 *
238 * @param objectType
239 * type code of the object to store.
240 * @param length
241 * number of bytes to scan from {@code in}.
242 * @param in
243 * stream providing the object content. The caller is responsible
244 * for closing the stream.
245 * @return the name of the object.
246 * @throws java.io.IOException
247 * the source stream could not be read.
248 */
249 public ObjectId idFor(int objectType, long length, InputStream in)
250 throws IOException {
251 SHA1 md = SHA1.newInstance();
252 md.update(Constants.encodedTypeString(objectType));
253 md.update((byte) ' ');
254 md.update(Constants.encodeASCII(length));
255 md.update((byte) 0);
256 byte[] buf = buffer();
257 while (length > 0) {
258 int n = in.read(buf, 0, (int) Math.min(length, buf.length));
259 if (n < 0)
260 throw new EOFException(JGitText.get().unexpectedEndOfInput);
261 md.update(buf, 0, n);
262 length -= n;
263 }
264 return md.toObjectId();
265 }
266
267 /**
268 * Compute the ObjectId for the given tree without inserting it.
269 *
270 * @param formatter
271 * a {@link org.eclipse.jgit.lib.TreeFormatter} object.
272 * @return the computed ObjectId
273 */
274 public ObjectId idFor(TreeFormatter formatter) {
275 return formatter.computeId(this);
276 }
277
278 /**
279 * Insert a single tree into the store, returning its unique name.
280 *
281 * @param formatter
282 * the formatter containing the proposed tree's data.
283 * @return the name of the tree object.
284 * @throws java.io.IOException
285 * the object could not be stored.
286 */
287 public final ObjectId insert(TreeFormatter formatter) throws IOException {
288 // Delegate to the formatter, as then it can pass the raw internal
289 // buffer back to this inserter, avoiding unnecessary data copying.
290 //
291 return formatter.insertTo(this);
292 }
293
294 /**
295 * Insert a single commit into the store, returning its unique name.
296 *
297 * @param builder
298 * the builder containing the proposed commit's data.
299 * @return the name of the commit object.
300 * @throws java.io.IOException
301 * the object could not be stored.
302 */
303 public final ObjectId insert(CommitBuilder builder) throws IOException {
304 return insert(Constants.OBJ_COMMIT, builder.build());
305 }
306
307 /**
308 * Insert a single annotated tag into the store, returning its unique name.
309 *
310 * @param builder
311 * the builder containing the proposed tag's data.
312 * @return the name of the tag object.
313 * @throws java.io.IOException
314 * the object could not be stored.
315 */
316 public final ObjectId insert(TagBuilder builder) throws IOException {
317 return insert(Constants.OBJ_TAG, builder.build());
318 }
319
320 /**
321 * Insert a single object into the store, returning its unique name.
322 *
323 * @param type
324 * type code of the object to store.
325 * @param data
326 * complete content of the object.
327 * @return the name of the object.
328 * @throws java.io.IOException
329 * the object could not be stored.
330 */
331 public ObjectId insert(int type, byte[] data)
332 throws IOException {
333 return insert(type, data, 0, data.length);
334 }
335
336 /**
337 * Insert a single object into the store, returning its unique name.
338 *
339 * @param type
340 * type code of the object to store.
341 * @param data
342 * complete content of the object.
343 * @param off
344 * first position within {@code data}.
345 * @param len
346 * number of bytes to copy from {@code data}.
347 * @return the name of the object.
348 * @throws java.io.IOException
349 * the object could not be stored.
350 */
351 public ObjectId insert(int type, byte[] data, int off, int len)
352 throws IOException {
353 return insert(type, len, new ByteArrayInputStream(data, off, len));
354 }
355
356 /**
357 * Insert a single object into the store, returning its unique name.
358 *
359 * @param objectType
360 * type code of the object to store.
361 * @param length
362 * number of bytes to copy from {@code in}.
363 * @param in
364 * stream providing the object content. The caller is responsible
365 * for closing the stream.
366 * @return the name of the object.
367 * @throws java.io.IOException
368 * the object could not be stored, or the source stream could
369 * not be read.
370 */
371 public abstract ObjectId insert(int objectType, long length, InputStream in)
372 throws IOException;
373
374 /**
375 * Initialize a parser to read from a pack formatted stream.
376 *
377 * @param in
378 * the input stream. The stream is not closed by the parser, and
379 * must instead be closed by the caller once parsing is complete.
380 * @return the pack parser.
381 * @throws java.io.IOException
382 * the parser instance, which can be configured and then used to
383 * parse objects into the ObjectDatabase.
384 */
385 public abstract PackParser newPackParser(InputStream in) throws IOException;
386
387 /**
388 * Open a reader for objects that may have been written by this inserter.
389 * <p>
390 * The returned reader allows the calling thread to read back recently
391 * inserted objects without first calling {@code flush()} to make them
392 * visible to the repository. The returned reader should only be used from
393 * the same thread as the inserter. Objects written by this inserter may not
394 * be visible to {@code this.newReader().newReader()}.
395 * <p>
396 * The returned reader should return this inserter instance from {@link
397 * ObjectReader#getCreatedFromInserter()}.
398 * <p>
399 * Behavior is undefined if an insert method is called on the inserter in the
400 * middle of reading from an {@link ObjectStream} opened from this reader. For
401 * example, reading the remainder of the object may fail, or newly written
402 * data may even be corrupted. Interleaving whole object reads (including
403 * streaming reads) with inserts is fine, just not interleaving streaming
404 * <em>partial</em> object reads with inserts.
405 *
406 * @since 3.5
407 * @return reader for any object, including an object recently inserted by
408 * this inserter since the last flush.
409 */
410 public abstract ObjectReader newReader();
411
412 /**
413 * Make all inserted objects visible.
414 * <p>
415 * The flush may take some period of time to make the objects available to
416 * other threads.
417 *
418 * @throws java.io.IOException
419 * the flush could not be completed; objects inserted thus far
420 * are in an indeterminate state.
421 */
422 public abstract void flush() throws IOException;
423
424 /**
425 * {@inheritDoc}
426 * <p>
427 * Release any resources used by this inserter.
428 * <p>
429 * An inserter that has been released can be used again, but may need to be
430 * released after the subsequent usage.
431 *
432 * @since 4.0
433 */
434 @Override
435 public abstract void close();
436 }