Correct old style loose object reader.
[egit/egit-new.git] / org.spearce.jgit / src / org / spearce / jgit / lib / UnpackedObjectLoader.java
blobeffcb20e1d6047888951cef45634bf26da72e87a
1 /*
2 * Copyright (C) 2006 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License, version 2.1, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.FileInputStream;
20 import java.io.IOException;
21 import java.util.zip.DataFormatException;
22 import java.util.zip.Inflater;
24 import org.spearce.jgit.errors.CorruptObjectException;
26 public class UnpackedObjectLoader extends ObjectLoader {
27 private final String objectType;
29 private final int objectSize;
31 private final byte[] bytes;
33 public UnpackedObjectLoader(final Repository db, final ObjectId id)
34 throws IOException {
35 final FileInputStream objStream = new FileInputStream(db.toFile(id));
36 final byte[] compressed;
37 try {
38 compressed = new byte[objStream.available()];
39 int off = 0;
40 while (off < compressed.length)
41 off += objStream.read(compressed, off, compressed.length - off);
42 } finally {
43 objStream.close();
45 setId(id);
47 // Try to determine if this is a legacy format loose object or
48 // a new style loose object. The legacy format was completely
49 // compressed with zlib so the first byte must be 0x78 (15-bit
50 // window size, deflated) and the first 16 bit word must be
51 // evenly divisible by 31. Otherwise its a new style loose
52 // object.
54 final int fb = compressed[0] & 0xff;
55 if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) {
56 final Inflater inflater = new Inflater(false);
57 inflater.setInput(compressed);
58 final byte[] hdr = new byte[64];
59 int avail = 0;
60 while (!inflater.finished() && avail < hdr.length)
61 try {
62 avail += inflater.inflate(hdr, avail, hdr.length - avail);
63 } catch (DataFormatException dfe) {
64 final CorruptObjectException coe;
65 coe = new CorruptObjectException(getId(), "bad stream");
66 coe.initCause(dfe);
67 inflater.end();
68 throw coe;
70 if (avail < 5)
71 throw new CorruptObjectException(id, "no header");
73 int pos;
74 switch (hdr[0]) {
75 case 'b':
76 if (hdr[1] != 'l' || hdr[2] != 'o' || hdr[3] != 'b'
77 || hdr[4] != ' ')
78 throw new CorruptObjectException(id, "invalid type");
79 objectType = Constants.TYPE_BLOB;
80 pos = 5;
81 break;
82 case 'c':
83 if (avail < 7 || hdr[1] != 'o' || hdr[2] != 'm'
84 || hdr[3] != 'm' || hdr[4] != 'i' || hdr[5] != 't'
85 || hdr[6] != ' ')
86 throw new CorruptObjectException(id, "invalid type");
87 objectType = Constants.TYPE_COMMIT;
88 pos = 7;
89 break;
90 case 't':
91 switch (hdr[1]) {
92 case 'a':
93 if (hdr[2] != 'g' || hdr[3] != ' ')
94 throw new CorruptObjectException(id, "invalid type");
95 objectType = Constants.TYPE_TAG;
96 pos = 4;
97 break;
98 case 'r':
99 if (hdr[2] != 'e' || hdr[3] != 'e' || hdr[4] != ' ')
100 throw new CorruptObjectException(id, "invalid type");
101 objectType = Constants.TYPE_TREE;
102 pos = 5;
103 break;
104 default:
105 throw new CorruptObjectException(id, "invalid type");
107 break;
108 default:
109 throw new CorruptObjectException(id, "invalid type");
112 int tempSize = 0;
113 while (pos < avail) {
114 final int c = hdr[pos++];
115 if (0 == c)
116 break;
117 else if (c < '0' || c > '9')
118 throw new CorruptObjectException(id, "invalid length");
119 tempSize *= 10;
120 tempSize += c - '0';
122 objectSize = tempSize;
123 bytes = new byte[objectSize];
124 if (pos < avail)
125 System.arraycopy(hdr, pos, bytes, 0, avail - pos);
126 decompress(inflater, avail);
127 } else {
128 int p = 0;
129 int c = compressed[p++] & 0xff;
130 final int typeCode = (c >> 4) & 7;
131 int size = c & 15;
132 int shift = 4;
133 while ((c & 0x80) != 0) {
134 c = compressed[p++] & 0xff;
135 size += (c & 0x7f) << shift;
136 shift += 7;
139 switch (typeCode) {
140 case Constants.OBJ_COMMIT:
141 objectType = Constants.TYPE_COMMIT;
142 break;
143 case Constants.OBJ_TREE:
144 objectType = Constants.TYPE_TREE;
145 break;
146 case Constants.OBJ_BLOB:
147 objectType = Constants.TYPE_BLOB;
148 break;
149 case Constants.OBJ_TAG:
150 objectType = Constants.TYPE_TAG;
151 break;
152 default:
153 throw new CorruptObjectException(id, "invalid type");
156 objectSize = size;
157 bytes = new byte[objectSize];
158 final Inflater inflater = new Inflater(false);
159 inflater.setInput(compressed, p, compressed.length - p);
160 decompress(inflater, 0);
164 private void decompress(final Inflater inf, int p) throws IOException {
165 try {
166 while (!inf.finished())
167 p += inf.inflate(bytes, p, objectSize - p);
168 } catch (DataFormatException dfe) {
169 final CorruptObjectException coe;
170 coe = new CorruptObjectException(getId(), "bad stream");
171 coe.initCause(dfe);
172 throw coe;
173 } finally {
174 inf.end();
176 if (p != objectSize)
177 new CorruptObjectException(getId(), "incorrect length");
180 public String getType() {
181 return objectType;
184 public long getSize() {
185 return objectSize;
188 public byte[] getBytes() {
189 return bytes;