1 /*
2 * Copyright (C) 2008-2009, Google Inc.
3 * Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk>
4 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
5 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
6 * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
7 * and other copyright owners as documented in the project's IP log.
8 *
9 * This program and the accompanying materials are made available
10 * under the terms of the Eclipse Distribution License v1.0 which
11 * accompanies this distribution, is reproduced below, and is
12 * available at http://www.eclipse.org/org/documents/edl-v10.php
13 *
14 * All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or
17 * without modification, are permitted provided that the following
18 * conditions are met:
19 *
20 * - Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 *
23 * - Redistributions in binary form must reproduce the above
24 * copyright notice, this list of conditions and the following
25 * disclaimer in the documentation and/or other materials provided
26 * with the distribution.
27 *
28 * - Neither the name of the Eclipse Foundation, Inc. nor the
29 * names of its contributors may be used to endorse or promote
30 * products derived from this software without specific prior
31 * written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
34 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
38 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
40 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
41 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
42 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
45 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 */
47
48 package org.eclipse.jgit.lib;
49
50 import java.io.EOFException;
51 import java.io.IOException;
52 import java.io.OutputStream;
53
54 import org.eclipse.jgit.errors.LargeObjectException;
55 import org.eclipse.jgit.errors.MissingObjectException;
56 import org.eclipse.jgit.util.IO;
57
58 /**
59 * Base class for a set of loaders for different representations of Git objects.
60 * New loaders are constructed for every object.
61 */
62 public abstract class ObjectLoader {
63 /**
64 * @return Git in pack object type, see {@link Constants}.
65 */
66 public abstract int getType();
67
68 /**
69 * @return size of object in bytes
70 */
71 public abstract long getSize();
72
73 /**
74 * @return true if this object is too large to obtain as a byte array.
75 * Objects over a certain threshold should be accessed only by their
76 * {@link #openStream()} to prevent overflowing the JVM heap.
77 */
78 public boolean isLarge() {
79 try {
80 getCachedBytes();
81 return false;
82 } catch (LargeObjectException tooBig) {
83 return true;
84 }
85 }
86
87 /**
88 * Obtain a copy of the bytes of this object.
89 * <p>
90 * Unlike {@link #getCachedBytes()} this method returns an array that might
91 * be modified by the caller.
92 *
93 * @return the bytes of this object.
94 * @throws LargeObjectException
95 * if the object won't fit into a byte array, because
96 * {@link #isLarge()} returns true. Callers should use
97 * {@link #openStream()} instead to access the contents.
98 */
99 public final byte[] getBytes() throws LargeObjectException {
100 return cloneArray(getCachedBytes());
101 }
102
103 /**
104 * Obtain a copy of the bytes of this object.
105 *
106 * If the object size is less than or equal to {@code sizeLimit} this method
107 * will provide it as a byte array, even if {@link #isLarge()} is true. This
108 * utility is useful for application code that absolutely must have the
109 * object as a single contiguous byte array in memory.
110 *
111 * Unlike {@link #getCachedBytes(int)} this method returns an array that
112 * might be modified by the caller.
113 *
114 * @param sizeLimit
115 * maximum number of bytes to return. If the object is larger
116 * than this limit, {@link LargeObjectException} will be thrown.
117 * @return the bytes of this object.
118 * @throws LargeObjectException
119 * if the object is bigger than {@code sizeLimit}, or if
120 * {@link OutOfMemoryError} occurs during allocation of the
121 * result array. Callers should use {@link #openStream()}
122 * instead to access the contents.
123 * @throws MissingObjectException
124 * the object is large, and it no longer exists.
125 * @throws IOException
126 * the object store cannot be accessed.
127 */
128 public final byte[] getBytes(int sizeLimit) throws LargeObjectException,
129 MissingObjectException, IOException {
130 byte[] cached = getCachedBytes(sizeLimit);
131 try {
132 return cloneArray(cached);
133 } catch (OutOfMemoryError tooBig) {
134 throw new LargeObjectException.OutOfMemory(tooBig);
135 }
136 }
137
138 /**
139 * Obtain a reference to the (possibly cached) bytes of this object.
140 * <p>
141 * This method offers direct access to the internal caches, potentially
142 * saving on data copies between the internal cache and higher level code.
143 * Callers who receive this reference <b>must not</b> modify its contents.
144 * Changes (if made) will affect the cache but not the repository itself.
145 *
146 * @return the cached bytes of this object. Do not modify it.
147 * @throws LargeObjectException
148 * if the object won't fit into a byte array, because
149 * {@link #isLarge()} returns true. Callers should use
150 * {@link #openStream()} instead to access the contents.
151 */
152 public abstract byte[] getCachedBytes() throws LargeObjectException;
153
154 /**
155 * Obtain a reference to the (possibly cached) bytes of this object.
156 *
157 * If the object size is less than or equal to {@code sizeLimit} this method
158 * will provide it as a byte array, even if {@link #isLarge()} is true. This
159 * utility is useful for application code that absolutely must have the
160 * object as a single contiguous byte array in memory.
161 *
162 * This method offers direct access to the internal caches, potentially
163 * saving on data copies between the internal cache and higher level code.
164 * Callers who receive this reference <b>must not</b> modify its contents.
165 * Changes (if made) will affect the cache but not the repository itself.
166 *
167 * @param sizeLimit
168 * maximum number of bytes to return. If the object size is
169 * larger than this limit and {@link #isLarge()} is true,
170 * {@link LargeObjectException} will be thrown.
171 * @return the cached bytes of this object. Do not modify it.
172 * @throws LargeObjectException
173 * if the object is bigger than {@code sizeLimit}, or if
174 * {@link OutOfMemoryError} occurs during allocation of the
175 * result array. Callers should use {@link #openStream()}
176 * instead to access the contents.
177 * @throws MissingObjectException
178 * the object is large, and it no longer exists.
179 * @throws IOException
180 * the object store cannot be accessed.
181 */
182 public byte[] getCachedBytes(int sizeLimit) throws LargeObjectException,
183 MissingObjectException, IOException {
184 if (!isLarge())
185 return getCachedBytes();
186
187 ObjectStream in = openStream();
188 try {
189 long sz = in.getSize();
190 if (sizeLimit < sz)
191 throw new LargeObjectException.ExceedsLimit(sizeLimit, sz);
192
193 if (Integer.MAX_VALUE < sz)
194 throw new LargeObjectException.ExceedsByteArrayLimit();
195
196 byte[] buf;
197 try {
198 buf = new byte[(int) sz];
199 } catch (OutOfMemoryError notEnoughHeap) {
200 throw new LargeObjectException.OutOfMemory(notEnoughHeap);
201 }
202
203 IO.readFully(in, buf, 0, buf.length);
204 return buf;
205 } finally {
206 in.close();
207 }
208 }
209
210 /**
211 * Obtain an input stream to read this object's data.
212 *
213 * @return a stream of this object's data. Caller must close the stream when
214 * through with it. The returned stream is buffered with a
215 * reasonable buffer size.
216 * @throws MissingObjectException
217 * the object no longer exists.
218 * @throws IOException
219 * the object store cannot be accessed.
220 */
221 public abstract ObjectStream openStream() throws MissingObjectException,
222 IOException;
223
224 /**
225 * Copy this object to the output stream.
226 * <p>
227 * For some object store implementations, this method may be more efficient
228 * than reading from {@link #openStream()} into a temporary byte array, then
229 * writing to the destination stream.
230 * <p>
231 * The default implementation of this method is to copy with a temporary
232 * byte array for large objects, or to pass through the cached byte array
233 * for small objects.
234 *
235 * @param out
236 * stream to receive the complete copy of this object's data.
237 * Caller is responsible for flushing or closing this stream
238 * after this method returns.
239 * @throws MissingObjectException
240 * the object no longer exists.
241 * @throws IOException
242 * the object store cannot be accessed, or the stream cannot be
243 * written to.
244 */
245 public void copyTo(OutputStream out) throws MissingObjectException,
246 IOException {
247 if (isLarge()) {
248 ObjectStream in = openStream();
249 try {
250 final long sz = in.getSize();
251 byte[] tmp = new byte[8192];
252 long copied = 0;
253 while (copied < sz) {
254 int n = in.read(tmp);
255 if (n < 0)
256 throw new EOFException();
257 out.write(tmp, 0, n);
258 copied += n;
259 }
260 if (0 <= in.read())
261 throw new EOFException();
262 } finally {
263 in.close();
264 }
265 } else {
266 out.write(getCachedBytes());
267 }
268 }
269
270 private static byte[] cloneArray(final byte[] data) {
271 final byte[] copy = new byte[data.length];
272 System.arraycopy(data, 0, copy, 0, data.length);
273 return copy;
274 }
275
276 /**
277 * Simple loader around the cached byte array.
278 * <p>
279 * ObjectReader implementations can use this stream type when the object's
280 * content is small enough to be accessed as a single byte array.
281 */
282 public static class SmallObject extends ObjectLoader {
283 private final int type;
284
285 private final byte[] data;
286
287 /**
288 * Construct a small object loader.
289 *
290 * @param type
291 * type of the object.
292 * @param data
293 * the object's data array. This array will be returned as-is
294 * for the {@link #getCachedBytes()} method.
295 */
296 public SmallObject(int type, byte[] data) {
297 this.type = type;
298 this.data = data;
299 }
300
301 @Override
302 public int getType() {
303 return type;
304 }
305
306 @Override
307 public long getSize() {
308 return getCachedBytes().length;
309 }
310
311 @Override
312 public boolean isLarge() {
313 return false;
314 }
315
316 @Override
317 public byte[] getCachedBytes() {
318 return data;
319 }
320
321 @Override
322 public ObjectStream openStream() {
323 return new ObjectStream.SmallStream(this);
324 }
325 }
326 }