Switch jgit library to the EDL (3-clause BSD)
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / lib / UnpackedObjectLoader.java
blob4e95387efa76b5d78e8d04e011c0957ab149f7ed
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
53 * stored in a pack.
55 public class UnpackedObjectLoader extends ObjectLoader {
56 private final int objectType;
58 private final int objectSize;
60 private final byte[] bytes;
62 /**
63 * Construct an ObjectLoader for the specified SHA-1
64 * @param db repository
65 * @param id SHA-1
66 * @throws IOException
68 public UnpackedObjectLoader(final Repository db, final ObjectId id)
69 throws IOException {
70 this(readCompressed(db, id), id);
73 private static byte[] readCompressed(final Repository db, final ObjectId id)
74 throws FileNotFoundException, IOException {
75 final FileInputStream objStream = new FileInputStream(db.toFile(id));
76 final byte[] compressed;
77 try {
78 compressed = new byte[objStream.available()];
79 int off = 0;
80 while (off < compressed.length)
81 off += objStream.read(compressed, off, compressed.length - off);
82 } finally {
83 objStream.close();
85 return compressed;
88 /**
89 * Construct an ObjectLoader from a loose object's compressed form.
91 * @param compressed
92 * entire content of the loose object file.
93 * @throws CorruptObjectException
94 * The compressed data supplied does not match the format for a
95 * valid loose object.
97 public UnpackedObjectLoader(final byte[] compressed) throws CorruptObjectException {
98 this(compressed, null);
101 private UnpackedObjectLoader(final byte[] compressed, final ObjectId id) throws CorruptObjectException {
102 setId(id);
104 // Try to determine if this is a legacy format loose object or
105 // a new style loose object. The legacy format was completely
106 // compressed with zlib so the first byte must be 0x78 (15-bit
107 // window size, deflated) and the first 16 bit word must be
108 // evenly divisible by 31. Otherwise its a new style loose
109 // object.
111 final Inflater inflater = InflaterCache.get();
112 try {
113 final int fb = compressed[0] & 0xff;
114 if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) {
115 inflater.setInput(compressed);
116 final byte[] hdr = new byte[64];
117 int avail = 0;
118 while (!inflater.finished() && avail < hdr.length)
119 try {
120 avail += inflater.inflate(hdr, avail, hdr.length
121 - avail);
122 } catch (DataFormatException dfe) {
123 final CorruptObjectException coe;
124 coe = new CorruptObjectException(id, "bad stream");
125 coe.initCause(dfe);
126 inflater.end();
127 throw coe;
129 if (avail < 5)
130 throw new CorruptObjectException(id, "no header");
132 final MutableInteger p = new MutableInteger();
133 objectType = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
134 objectSize = RawParseUtils.parseBase10(hdr, p.value, p);
135 if (objectSize < 0)
136 throw new CorruptObjectException(id, "negative size");
137 if (hdr[p.value++] != 0)
138 throw new CorruptObjectException(id, "garbage after size");
139 bytes = new byte[objectSize];
140 if (p.value < avail)
141 System.arraycopy(hdr, p.value, bytes, 0, avail - p.value);
142 decompress(id, inflater, avail - p.value);
143 } else {
144 int p = 0;
145 int c = compressed[p++] & 0xff;
146 final int typeCode = (c >> 4) & 7;
147 int size = c & 15;
148 int shift = 4;
149 while ((c & 0x80) != 0) {
150 c = compressed[p++] & 0xff;
151 size += (c & 0x7f) << shift;
152 shift += 7;
155 switch (typeCode) {
156 case Constants.OBJ_COMMIT:
157 case Constants.OBJ_TREE:
158 case Constants.OBJ_BLOB:
159 case Constants.OBJ_TAG:
160 objectType = typeCode;
161 break;
162 default:
163 throw new CorruptObjectException(id, "invalid type");
166 objectSize = size;
167 bytes = new byte[objectSize];
168 inflater.setInput(compressed, p, compressed.length - p);
169 decompress(id, inflater, 0);
171 } finally {
172 InflaterCache.release(inflater);
176 private void decompress(final ObjectId id, final Inflater inf, int p)
177 throws CorruptObjectException {
178 try {
179 while (!inf.finished())
180 p += inf.inflate(bytes, p, objectSize - p);
181 } catch (DataFormatException dfe) {
182 final CorruptObjectException coe;
183 coe = new CorruptObjectException(id, "bad stream");
184 coe.initCause(dfe);
185 throw coe;
187 if (p != objectSize)
188 new CorruptObjectException(id, "incorrect length");
191 public int getType() {
192 return objectType;
195 public long getSize() {
196 return objectSize;
199 public byte[] getBytes() {
200 return bytes;
203 @Override
204 public byte[] getCachedBytes() throws IOException {
205 return bytes;