Fix UnpackedObjectLoader.getBytes to return a copy
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / UnpackedObjectLoader.java
blob3ad273f8cd8b03c612836565c33e43b5650c8fa5
1 /*
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.lib;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.IOException;
44 import java.util.zip.DataFormatException;
45 import java.util.zip.Inflater;
47 import org.spearce.jgit.errors.CorruptObjectException;
48 import org.spearce.jgit.util.MutableInteger;
49 import org.spearce.jgit.util.RawParseUtils;
51 /**
52 * Loose object loader. This class loads an object not stored in a pack.
54 public class UnpackedObjectLoader extends ObjectLoader {
55 private final int objectType;
57 private final int objectSize;
59 private final byte[] bytes;
61 /**
62 * Construct an ObjectLoader for the specified SHA-1
64 * @param db
65 * repository
66 * @param id
67 * SHA-1
68 * @throws IOException
70 public UnpackedObjectLoader(final Repository db, final ObjectId id)
71 throws IOException {
72 this(readCompressed(db, id), id);
75 private static byte[] readCompressed(final Repository db, final ObjectId id)
76 throws FileNotFoundException, IOException {
77 final FileInputStream objStream = new FileInputStream(db.toFile(id));
78 final byte[] compressed;
79 try {
80 compressed = new byte[objStream.available()];
81 int off = 0;
82 while (off < compressed.length)
83 off += objStream.read(compressed, off, compressed.length - off);
84 } finally {
85 objStream.close();
87 return compressed;
90 /**
91 * Construct an ObjectLoader from a loose object's compressed form.
93 * @param compressed
94 * entire content of the loose object file.
95 * @throws CorruptObjectException
96 * The compressed data supplied does not match the format for a
97 * valid loose object.
99 public UnpackedObjectLoader(final byte[] compressed)
100 throws CorruptObjectException {
101 this(compressed, null);
104 private UnpackedObjectLoader(final byte[] compressed, final ObjectId id)
105 throws CorruptObjectException {
106 setId(id);
108 // Try to determine if this is a legacy format loose object or
109 // a new style loose object. The legacy format was completely
110 // compressed with zlib so the first byte must be 0x78 (15-bit
111 // window size, deflated) and the first 16 bit word must be
112 // evenly divisible by 31. Otherwise its a new style loose
113 // object.
115 final Inflater inflater = InflaterCache.get();
116 try {
117 final int fb = compressed[0] & 0xff;
118 if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) {
119 inflater.setInput(compressed);
120 final byte[] hdr = new byte[64];
121 int avail = 0;
122 while (!inflater.finished() && avail < hdr.length)
123 try {
124 avail += inflater.inflate(hdr, avail, hdr.length
125 - avail);
126 } catch (DataFormatException dfe) {
127 final CorruptObjectException coe;
128 coe = new CorruptObjectException(id, "bad stream");
129 coe.initCause(dfe);
130 inflater.end();
131 throw coe;
133 if (avail < 5)
134 throw new CorruptObjectException(id, "no header");
136 final MutableInteger p = new MutableInteger();
137 objectType = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
138 objectSize = RawParseUtils.parseBase10(hdr, p.value, p);
139 if (objectSize < 0)
140 throw new CorruptObjectException(id, "negative size");
141 if (hdr[p.value++] != 0)
142 throw new CorruptObjectException(id, "garbage after size");
143 bytes = new byte[objectSize];
144 if (p.value < avail)
145 System.arraycopy(hdr, p.value, bytes, 0, avail - p.value);
146 decompress(id, inflater, avail - p.value);
147 } else {
148 int p = 0;
149 int c = compressed[p++] & 0xff;
150 final int typeCode = (c >> 4) & 7;
151 int size = c & 15;
152 int shift = 4;
153 while ((c & 0x80) != 0) {
154 c = compressed[p++] & 0xff;
155 size += (c & 0x7f) << shift;
156 shift += 7;
159 switch (typeCode) {
160 case Constants.OBJ_COMMIT:
161 case Constants.OBJ_TREE:
162 case Constants.OBJ_BLOB:
163 case Constants.OBJ_TAG:
164 objectType = typeCode;
165 break;
166 default:
167 throw new CorruptObjectException(id, "invalid type");
170 objectSize = size;
171 bytes = new byte[objectSize];
172 inflater.setInput(compressed, p, compressed.length - p);
173 decompress(id, inflater, 0);
175 } finally {
176 InflaterCache.release(inflater);
180 private void decompress(final ObjectId id, final Inflater inf, int p)
181 throws CorruptObjectException {
182 try {
183 while (!inf.finished())
184 p += inf.inflate(bytes, p, objectSize - p);
185 } catch (DataFormatException dfe) {
186 final CorruptObjectException coe;
187 coe = new CorruptObjectException(id, "bad stream");
188 coe.initCause(dfe);
189 throw coe;
191 if (p != objectSize)
192 new CorruptObjectException(id, "incorrect length");
195 public int getType() {
196 return objectType;
199 public long getSize() {
200 return objectSize;
203 @Override
204 public byte[] getCachedBytes() throws IOException {
205 return bytes;
208 @Override
209 public int getRawType() {
210 return objectType;
213 @Override
214 public long getRawSize() {
215 return objectSize;