Commit Diff


commit - 9710aac2fcc11960009a5aad05cda68a89e85b7a
commit + b107e67fba94d3a78c6b93b0d9a0f3482c9e5dd9
blob - 204947b421455ff730a46fe0ff59b72331ec8a6a
blob + 54b890a4cdf611d89cafac33844289083e0fc554
--- include/got_object.h
+++ include/got_object.h
@@ -80,15 +80,20 @@ struct got_object {
 	size_t size;
 	struct got_object_id id;
 
-	char *path_packfile;
-	off_t pack_offset;
+	char *path_packfile;	/* if packed */
+	off_t pack_offset;	/* if packed */
+
+	/* If type is OFFSET_DELTA: */
+	int base_type;
+	uint64_t base_size;
+	off_t base_obj_offset;
 };
 
 struct got_repository;
 
 char *got_object_id_str(struct got_object_id *, char *, size_t);
 int got_object_id_cmp(struct got_object_id *, struct got_object_id *);
-const char *got_object_get_type_tag(int);
+int got_object_get_type(struct got_object *);
 const struct got_error *got_object_open(struct got_object **,
     struct got_repository *, struct got_object_id *);
 void got_object_close(struct got_object *);
blob - d99ed9f7c19a52512573c812b0f54ccfe0218861
blob + 3bf0c3c7eaa09b84e6b176a6d650cd3e57ef42ae
--- lib/diff.c
+++ lib/diff.c
@@ -232,7 +232,7 @@ diff_added_tree(struct got_object_id *id, struct got_r
 	if (err)
 		goto done;
 
-	if (treeobj->type != GOT_OBJ_TYPE_TREE) {
+	if (got_object_get_type(treeobj) != GOT_OBJ_TYPE_TREE) {
 		err = got_error(GOT_ERR_OBJ_TYPE);
 		goto done;
 	}
@@ -312,7 +312,7 @@ diff_deleted_tree(struct got_object_id *id, struct got
 	if (err)
 		goto done;
 
-	if (treeobj->type != GOT_OBJ_TYPE_TREE) {
+	if (got_object_get_type(treeobj) != GOT_OBJ_TYPE_TREE) {
 		err = got_error(GOT_ERR_OBJ_TYPE);
 		goto done;
 	}
blob - 96d336a171264be9c2e02e38fbaa135a7859c3aa
blob + 9f2bc6f19f925498fcf0679ec4e0b0a4deeee9d8
--- lib/object.c
+++ lib/object.c
@@ -61,19 +61,21 @@ got_object_id_cmp(struct got_object_id *id1, struct go
 	return memcmp(id1->sha1, id2->sha1, SHA1_DIGEST_LENGTH);
 }
 
-const char *
-got_object_get_type_tag(int type)
+int
+got_object_get_type(struct got_object *obj)
 {
-	switch (type) {
+	switch (obj->type) {
 	case GOT_OBJ_TYPE_COMMIT:
-		return GOT_OBJ_TAG_COMMIT;
 	case GOT_OBJ_TYPE_TREE:
-		return GOT_OBJ_TAG_TREE;
 	case GOT_OBJ_TYPE_BLOB:
-		return GOT_OBJ_TAG_BLOB;
+	case GOT_OBJ_TYPE_TAG:
+		return obj->type;
+	case GOT_OBJ_TYPE_REF_DELTA:
+	case GOT_OBJ_TYPE_OFFSET_DELTA:
+		return obj->base_type;
 	}
 
-	return NULL;
+	abort();
 }
 
 static void
blob - 3ec264fcdf777cbd3e6b7dabb936c418472ef058
blob + a91b59411f042723dc0f4b7c23bb54b076ea2bd1
--- lib/pack.c
+++ lib/pack.c
@@ -383,11 +383,103 @@ open_plain_object(struct got_object **obj, const char 
 	(*obj)->size = size;
 	memcpy(&(*obj)->id, id, sizeof((*obj)->id));
 	(*obj)->pack_offset = offset;
+
+	return NULL;
+}
+
+static const struct got_error *
+decode_negative_offset(int64_t *offset, size_t *len, FILE *packfile)
+{
+	int64_t o = 0;
+	uint8_t offN;
+	size_t n;
+	int i = 0;
+
+	do {
+		/* We do not support offset values which don't fit in 64 bit. */
+		if (i > 8)
+			return got_error(GOT_ERR_NO_SPACE);
+
+		n = fread(&offN, sizeof(offN), 1, packfile);
+		if (n != 1)
+			return got_ferror(packfile, GOT_ERR_BAD_PACKIDX);
+
+		if (i == 0)
+			o = (offN & GOT_PACK_OBJ_DELTA_OFF_VAL_MASK);
+		else {
+			int j;
+			int64_t v = 128;
+			o <<= 7;
+			o |= (offN & GOT_PACK_OBJ_DELTA_OFF_VAL_MASK);
+			o += v;
+			for (j = 0; j < i; j++) {
+				v <<= 7;
+				o += v;
+			}
+		}
+		i++;
+	} while (offN & GOT_PACK_OBJ_DELTA_OFF_MORE);
 
+	*offset = o;
+	*len = i * sizeof(offN);
 	return NULL;
 }
 
 static const struct got_error *
+open_offset_delta_object(struct got_object **obj, struct got_repository *repo,
+    const char *path_packfile, FILE *packfile, struct got_object_id *id,
+    off_t offset, size_t size)
+{
+	const struct got_error *err = NULL;
+	int64_t negoffset;
+	size_t negofflen;
+	off_t base_obj_offset;
+	struct got_object *base_obj;
+	struct got_object_id base_id;
+	uint8_t base_type;
+	uint64_t base_size;
+	size_t base_tslen;
+
+	err = decode_negative_offset(&negoffset, &negofflen, packfile);
+	if (err)
+		return err;
+
+	/* Compute the base object's offset (must be in the same pack file). */
+	base_obj_offset = (offset - negoffset);
+	if (base_obj_offset <= 0)
+		return got_error(GOT_ERR_BAD_PACKFILE);
+
+	if (fseeko(packfile, base_obj_offset, SEEK_SET) != 0)
+		return got_error_from_errno();
+
+	err = decode_type_and_size(&base_type, &base_size, &base_tslen,
+	    packfile);
+	if (err)
+		return err;
+
+	*obj = calloc(1, sizeof(**obj));
+	if (*obj == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+
+	(*obj)->path_packfile = strdup(path_packfile);
+	if ((*obj)->path_packfile == NULL) {
+		free(*obj);
+		return got_error(GOT_ERR_NO_MEM);
+	}
+	(*obj)->type = GOT_OBJ_TYPE_OFFSET_DELTA;
+	(*obj)->flags = GOT_OBJ_FLAG_PACKED;
+	(*obj)->hdrlen = 0;
+	(*obj)->size = size;
+	memcpy(&(*obj)->id, id, sizeof((*obj)->id));
+	(*obj)->pack_offset = offset;
+	(*obj)->base_type = base_type;
+	(*obj)->base_size = base_size;
+	(*obj)->base_obj_offset = base_obj_offset;
+
+	return NULL;
+}
+
+static const struct got_error *
 open_packed_object(struct got_object **obj, struct got_repository *repo,
     const char *path_packdir, struct got_packidx_v2_hdr *packidx,
     struct got_object_id *id)
@@ -447,9 +539,14 @@ open_packed_object(struct got_object **obj, struct got
 		    offset + tslen, size);
 		break;
 
+	case GOT_OBJ_TYPE_OFFSET_DELTA:
+		err = open_offset_delta_object(obj, repo, path_packfile,
+		    packfile, id, offset + tslen, size);
+		break;
+
 	case GOT_OBJ_TYPE_REF_DELTA:
 	case GOT_OBJ_TYPE_TAG:
-	case GOT_OBJ_TYPE_OFFSET_DELTA:
+		break;
 	default:
 		err = got_error(GOT_ERR_NOT_IMPL);
 		goto done;
@@ -547,7 +644,7 @@ dump_ref_delta_object(struct got_repository *repo, FIL
 	const struct got_error *err = NULL;
 	struct got_object_id base_id;
 	struct got_object *base_obj;
-	int n;
+	size_t n;
 
 	if (size < sizeof(base_id))
 		return got_ferror(infile, GOT_ERR_BAD_PACKFILE);
blob - 18a6071e88706cf03a4eb280c3b30f5cd73fc425
blob + 189b5641714068ed3ea3bc6d13b1f765956a582d
--- lib/pack.h
+++ lib/pack.h
@@ -107,6 +107,9 @@ struct got_packfile_object_data_offset_delta {
 	 * 2^14 + ... + 2^(7 * (n-1)) is added to the result.
 	 */
 	uint8_t *offset;	/* variable length */
+#define GOT_PACK_OBJ_DELTA_OFF_MORE		0x80
+#define GOT_PACK_OBJ_DELTA_OFF_VAL_MASK		0x7f
+	uint8_t *delta_data;		/* compressed */
 };
 
 struct got_packfile_obj_data {
blob - 8fd30992c9434f52ee5debdc68bdf0bbf2a550c3
blob + 6495ec45fdd21d9b0882c3766a0ff40302a76137
--- regress/repository/repository_test.c
+++ regress/repository/repository_test.c
@@ -49,7 +49,7 @@ print_parent_commits(struct got_commit_object *commit,
 		err = got_object_open(&obj, repo, &pid->id);
 		if (err != NULL)
 			return err;
-		if (obj->type != GOT_OBJ_TYPE_COMMIT)
+		if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT)
 			return got_error(GOT_ERR_OBJ_TYPE);
 		print_commit_object(obj, repo);
 		got_object_close(obj);
@@ -89,7 +89,7 @@ print_tree_object(struct got_object *obj, char *parent
 		if (err != NULL)
 			break;
 
-		if (treeobj->type != GOT_OBJ_TYPE_TREE) {
+		if (got_object_get_type(treeobj) != GOT_OBJ_TYPE_TREE) {
 			err = got_error(GOT_ERR_OBJ_TYPE);
 			got_object_close(treeobj);
 			break;
@@ -142,7 +142,7 @@ print_commit_object(struct got_object *obj, struct got
 	err = got_object_open(&treeobj, repo, &commit->tree_id);
 	if (err != NULL)
 		return err;
-	if (treeobj->type == GOT_OBJ_TYPE_TREE) {
+	if (got_object_get_type(treeobj) == GOT_OBJ_TYPE_TREE) {
 		print_tree_object(treeobj, "", repo);
 		printf("\n");
 	}
@@ -178,7 +178,7 @@ repo_read_log(const char *repo_path)
 	err = got_object_open(&obj, repo, id);
 	if (err != NULL || obj == NULL)
 		return 0;
-	if (obj->type == GOT_OBJ_TYPE_COMMIT)
+	if (got_object_get_type(obj) == GOT_OBJ_TYPE_COMMIT)
 		print_commit_object(obj, repo);
 	got_object_close(obj);
 	free(id);
@@ -209,7 +209,7 @@ repo_read_blob(const char *repo_path)
 	err = got_object_open(&obj, repo, &id);
 	if (err != NULL || obj == NULL)
 		return 0;
-	if (obj->type != GOT_OBJ_TYPE_BLOB)
+	if (got_object_get_type(obj) != GOT_OBJ_TYPE_BLOB)
 		return 0;
 
 	err = got_object_blob_open(&blob, repo, obj, 64);