+/* Returns 0 on fast-path success, returns 1 on deltified
+ * and need to unpack to see info.
+ */
+static int packed_object_info(struct pack_entry *entry,
+ char *type, unsigned long *sizep)
+{
+ struct packed_git *p = entry->p;
+ unsigned long offset, size, left;
+ unsigned char *pack;
+
+ offset = entry->offset;
+ if (p->pack_size - 5 < offset)
+ die("object offset outside of pack file");
+ pack = p->pack_base + offset;
+ size = (pack[1] << 24) + (pack[2] << 16) + (pack[3] << 8) + pack[4];
+ left = p->pack_size - offset - 5;
+ switch (*pack) {
+ case 'D':
+ return 1;
+ break;
+ case 'C':
+ strcpy(type, "commit");
+ break;
+ case 'T':
+ strcpy(type, "tree");
+ break;
+ case 'B':
+ strcpy(type, "blob");
+ break;
+ default:
+ die("corrupted pack file");
+ }
+ *sizep = size;
+ return 0;
+}
+
+/* forward declaration for a mutually recursive function */
+static void *unpack_entry(struct pack_entry *, char *, unsigned long *);
+
+static void *unpack_delta_entry(unsigned char *base_sha1,
+ unsigned long delta_size,
+ unsigned long left,
+ char *type,
+ unsigned long *sizep)
+{
+ void *data, *delta_data, *result, *base;
+ unsigned long data_size, result_size, base_size;
+ z_stream stream;
+ int st;
+
+ if (left < 20)
+ die("truncated pack file");
+ data = base_sha1 + 20;
+ data_size = left - 20;
+ delta_data = xmalloc(delta_size);
+
+ memset(&stream, 0, sizeof(stream));
+
+ stream.next_in = data;
+ stream.avail_in = data_size;
+ stream.next_out = delta_data;
+ stream.avail_out = delta_size;
+
+ inflateInit(&stream);
+ st = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
+ if ((st != Z_STREAM_END) || stream.total_out != delta_size)
+ die("delta data unpack failed");
+
+ /* This may recursively unpack the base, which is what we want */
+ base = read_sha1_file(base_sha1, type, &base_size);
+ if (!base)
+ die("failed to read delta-pack base object %s",
+ sha1_to_hex(base_sha1));
+ result = patch_delta(base, base_size,
+ delta_data, delta_size,
+ &result_size);
+ if (!result)
+ die("failed to apply delta");
+ free(delta_data);
+ free(base);
+ *sizep = result_size;
+ return result;
+}
+
+static void *unpack_non_delta_entry(unsigned char *data,
+ unsigned long size,
+ unsigned long left)
+{
+ int st;
+ z_stream stream;
+ char *buffer;
+
+ buffer = xmalloc(size + 1);
+ buffer[size] = 0;
+ memset(&stream, 0, sizeof(stream));
+ stream.next_in = data;
+ stream.avail_in = left;
+ stream.next_out = buffer;
+ stream.avail_out = size;
+
+ inflateInit(&stream);
+ st = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
+ if ((st != Z_STREAM_END) || stream.total_out != size) {
+ free(buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static void *unpack_entry(struct pack_entry *entry,
+ char *type, unsigned long *sizep)
+{
+ struct packed_git *p = entry->p;
+ unsigned long offset, size, left;
+ unsigned char *pack;
+
+ offset = entry->offset;
+ if (p->pack_size - 5 < offset)
+ die("object offset outside of pack file");
+
+ if (use_packed_git(p))
+ die("cannot map packed file");
+
+ pack = p->pack_base + offset;
+ size = (pack[1] << 24) + (pack[2] << 16) + (pack[3] << 8) + pack[4];
+ left = p->pack_size - offset - 5;
+ switch (*pack) {
+ case 'D':
+ return unpack_delta_entry(pack+5, size, left, type, sizep);
+ case 'C':
+ strcpy(type, "commit");
+ break;
+ case 'T':
+ strcpy(type, "tree");
+ break;
+ case 'B':
+ strcpy(type, "blob");
+ break;
+ default:
+ die("corrupted pack file");
+ }
+ *sizep = size;
+ return unpack_non_delta_entry(pack+5, size, left);
+}
+
+static int find_pack_entry_1(const unsigned char *sha1,
+ struct pack_entry *e, struct packed_git *p)
+{
+ int *level1_ofs = p->index_base;
+ int hi = ntohl(level1_ofs[*sha1]);
+ int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
+ void *index = p->index_base + 256;
+
+ do {
+ int mi = (lo + hi) / 2;
+ int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
+ if (!cmp) {
+ e->offset = ntohl(*((int*)(index + 24 * mi)));
+ memcpy(e->sha1, sha1, 20);
+ e->p = p;
+ return 1;
+ }
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi+1;
+ } while (lo < hi);
+ return 0;
+}
+
+static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+{
+ struct packed_git *p;
+ prepare_packed_git();
+
+ for (p = packed_git; p; p = p->next) {
+ if (find_pack_entry_1(sha1, e, p))
+ return 1;
+ }
+ return 0;
+}
+