Commit c6ac28fd by Russell Belfer

Reorg internal odb read header and object lookup

Often `git_odb_read_header` will "fail" and have to read the
entire object into memory instead of just the header.  When this
happens, the object is loaded and then disposed of immediately,
which makes it difficult to efficiently use the header information
to decide if the object should be loaded (since attempting to do
so will often result in loading the object twice).

This commit takes the existing code and reorganizes it to have
two new functions:

- `git_odb__read_header_or_object` which acts just like the old
  read header function except that it returns the object, too, if
  it was forced to load the whole thing.  It then becomes the
  callers responsibility to free the `git_odb_object`.
- `git_object__from_odb_object` which was extracted from the old
  `git_object_lookup` and creates a subclass of `git_object` from
  an existing `git_odb_object` (separating the ODB lookup from the
  `git_object` creation).  This allows you to use the first header
  reading function efficiently without instantiating the
  `git_odb_object` twice.

There is no net change to the behavior of any of the existing
functions, but this allows internal code to tap into the ODB
lookup and object creation to be more efficient.
parent e597b189
......@@ -292,6 +292,7 @@ static int get_blob_content(
git_blob **blob)
{
int error;
git_odb_object *odb_obj = NULL;
if (git_oid_iszero(&file->oid))
return 0;
......@@ -303,7 +304,8 @@ static int get_blob_content(
/* peek at object header to avoid loading if too large */
if ((error = git_repository_odb__weakptr(&odb, ctxt->repo)) < 0 ||
(error = git_odb_read_header(&len, &type, odb, &file->oid)) < 0)
(error = git_odb__read_header_or_object(
&odb_obj, &len, &type, odb, &file->oid)) < 0)
return error;
assert(type == GIT_OBJ_BLOB);
......@@ -317,7 +319,14 @@ static int get_blob_content(
if (ctxt->delta->binary == 1)
return 0;
if ((error = git_blob_lookup(blob, ctxt->repo, &file->oid)) < 0)
if (odb_obj != NULL) {
error = git_object__from_odb_object(
(git_object **)blob, ctxt->repo, odb_obj, GIT_OBJ_BLOB);
git_odb_object_free(odb_obj);
} else
error = git_blob_lookup(blob, ctxt->repo, &file->oid);
if (error)
return error;
map->data = (void *)git_blob_rawcontent(*blob);
......
......@@ -77,6 +77,58 @@ static int create_object(git_object **object_out, git_otype type)
return 0;
}
int git_object__from_odb_object(
git_object **object_out,
git_repository *repo,
git_odb_object *odb_obj,
git_otype type)
{
int error;
git_object *object = NULL;
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
giterr_set(GITERR_ODB, "The requested type does not match the type in the ODB");
return GIT_ENOTFOUND;
}
type = odb_obj->raw.type;
if ((error = create_object(&object, type)) < 0)
return error;
/* Initialize parent object */
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
object->repo = repo;
switch (type) {
case GIT_OBJ_COMMIT:
error = git_commit__parse((git_commit *)object, odb_obj);
break;
case GIT_OBJ_TREE:
error = git_tree__parse((git_tree *)object, odb_obj);
break;
case GIT_OBJ_TAG:
error = git_tag__parse((git_tag *)object, odb_obj);
break;
case GIT_OBJ_BLOB:
error = git_blob__parse((git_blob *)object, odb_obj);
break;
default:
break;
}
if (error < 0)
git_object__free(object);
else
*object_out = git_cache_try_store(&repo->objects, object);
return error;
}
int git_object_lookup_prefix(
git_object **object_out,
git_repository *repo,
......@@ -148,53 +200,11 @@ int git_object_lookup_prefix(
if (error < 0)
return error;
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
git_odb_object_free(odb_obj);
giterr_set(GITERR_ODB, "The given type does not match the type on the ODB");
return GIT_ENOTFOUND;
}
type = odb_obj->raw.type;
if (create_object(&object, type) < 0) {
git_odb_object_free(odb_obj);
return -1;
}
/* Initialize parent object */
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
object->repo = repo;
switch (type) {
case GIT_OBJ_COMMIT:
error = git_commit__parse((git_commit *)object, odb_obj);
break;
case GIT_OBJ_TREE:
error = git_tree__parse((git_tree *)object, odb_obj);
break;
case GIT_OBJ_TAG:
error = git_tag__parse((git_tag *)object, odb_obj);
break;
case GIT_OBJ_BLOB:
error = git_blob__parse((git_blob *)object, odb_obj);
break;
default:
break;
}
error = git_object__from_odb_object(object_out, repo, odb_obj, type);
git_odb_object_free(odb_obj);
if (error < 0) {
git_object__free(object);
return -1;
}
*object_out = git_cache_try_store(&repo->objects, object);
return 0;
return error;
}
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_object_h__
#define INCLUDE_object_h__
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
git_otype type;
};
/* fully free the object; internal method, DO NOT EXPORT */
void git_object__free(void *object);
GIT_INLINE(int) git_object__dup(git_object **dest, git_object *source)
{
git_cached_obj_incref(source);
*dest = source;
return 0;
}
int git_object__from_odb_object(
git_object **object_out,
git_repository *repo,
git_odb_object *odb_obj,
git_otype type);
int git_object__resolve_to_type(git_object **obj, git_otype type);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
#endif
......@@ -514,19 +514,36 @@ int git_odb_exists(git_odb *db, const git_oid *id)
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{
int error;
git_odb_object *object;
error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
if (object)
git_odb_object_free(object);
return error;
}
int git_odb__read_header_or_object(
git_odb_object **out, size_t *len_p, git_otype *type_p,
git_odb *db, const git_oid *id)
{
unsigned int i;
int error = GIT_ENOTFOUND;
git_odb_object *object;
assert(db && id);
assert(db && id && out && len_p && type_p);
if ((object = git_cache_get(&db->cache, id)) != NULL) {
*len_p = object->raw.len;
*type_p = object->raw.type;
git_odb_object_free(object);
*out = object;
return 0;
}
*out = NULL;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
......@@ -547,7 +564,8 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
*len_p = object->raw.len;
*type_p = object->raw.type;
git_odb_object_free(object);
*out = object;
return 0;
}
......
......@@ -84,4 +84,12 @@ int git_odb__error_notfound(const char *message, const git_oid *oid);
*/
int git_odb__error_ambiguous(const char *message);
/*
* Attempt to read object header or just return whole object if it could
* not be read.
*/
int git_odb__read_header_or_object(
git_odb_object **out, size_t *len_p, git_otype *type_p,
git_odb *db, const git_oid *id);
#endif
......@@ -18,6 +18,7 @@
#include "refs.h"
#include "buffer.h"
#include "odb.h"
#include "object.h"
#include "attr.h"
#include "strmap.h"
......@@ -75,13 +76,6 @@ enum {
GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18),
};
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
git_otype type;
};
/** Internal structure for repository object */
struct git_repository {
git_odb *_odb;
......@@ -102,21 +96,6 @@ struct git_repository {
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
/* fully free the object; internal method, DO NOT EXPORT */
void git_object__free(void *object);
GIT_INLINE(int) git_object__dup(git_object **dest, git_object *source)
{
git_cached_obj_incref(source);
*dest = source;
return 0;
}
int git_object__resolve_to_type(git_object **obj, git_otype type);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
{
return &repo->attrcache;
......@@ -136,7 +115,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
/*
* CVAR cache
* CVAR cache
*
* Efficient access to the most used config variables of a repository.
* The cache is cleared everytime the config backend is replaced.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment