Commit 3315782c by Vicent Marti

Redesigned the walking/object lookup interface

The old 'git_revpool' object has been removed and
split into two distinct objects with separate
functionality, in order to have separate methods for
object management and object walking.

*	A new object 'git_repository' does the high-level
	management of a repository's objects (commits, trees,
	tags, etc) on top of a 'git_odb'.

	Eventually, it will also manage other repository
	attributes (e.g. tag resolution, references, etc).

	See: src/git/repository.h

*	A new external method
		'git_repository_lookup(repo, oid, type)'
	has been added to the 'git_repository' API.

	All object lookups (git_XXX_lookup()) are now
	wrappers to this method, and duplicated code
	has been removed. The method does automatic type
	checking and returns a generic 'git_revpool_object'
	that can be cast to any specific object.

	See: src/git/repository.h

*	The external methods for object parsing of repository
	objects (git_XXX_parse()) have been removed.

	Loading objects from the repository is now managed
	through the 'lookup' functions. These objects are
	loaded with minimal information, and the relevant
	parsing is done automatically when the user requests
	any of the parsed attributes through accessor methods.

	An attribute has been added to 'git_repository' in
	order to force the parsing of all the repository objects
	immediately after lookup.

	See: src/git/commit.h
	See: src/git/tag.h
	See: src/git/tree.h

*	The previous walking functionality of the revpool
	is now found in 'git_revwalk', which does the actual
	revision walking on a repository; the attributes
	when walking through commits in a database have been
	decoupled from the actual commit objects.
	This increases performance when accessing commits
	during the walk and allows to have several
	'git_revwalk' instances working at the same time on
	top of the same repository, without having to load
	commits in memory several times.

	See: src/git/revwalk.h

*	The old 'git_revpool_table' has been renamed to
	'git_hashtable' and now works as a generic hashtable
	with support for any kind of object and custom hash
	functions.

	See: src/hashtable.h

*	All the relevant unit tests have been updated, renamed
	and grouped accordingly.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent f8758044
......@@ -27,6 +27,7 @@
#include "commit.h"
#include "revwalk.h"
#include "git/odb.h"
#include "git/repository.h"
#define COMMIT_PRINT(commit) {\
char oid[41]; oid[40] = 0;\
......@@ -34,9 +35,23 @@
printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
}
static void clear_parents(git_commit *commit)
{
git_commit_parents *node, *next_node;
node = commit->parents;
while (node) {
next_node = node->next;
free(node);
node = next_node;
}
commit->parents = NULL;
}
void git_commit__free(git_commit *commit)
{
git_commit_list_clear(&commit->parents, 0);
clear_parents(commit);
if (commit->odb_open)
git_obj_close(&commit->odb_object);
......@@ -53,47 +68,12 @@ const git_oid *git_commit_id(git_commit *c)
return &c->object.id;
}
void git_commit__mark_uninteresting(git_commit *commit)
{
git_commit_node *parents;
if (commit == NULL)
return;
parents = commit->parents.head;
commit->uninteresting = 1;
while (parents) {
parents->commit->uninteresting = 1;
parents = parents->next;
}
}
git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
{
git_commit *commit = NULL;
if ((commit = git_commit_lookup(pool, id)) == NULL)
return NULL;
if (git_commit__parse_basic(commit) < 0)
goto error_cleanup;
return commit;
error_cleanup:
/* FIXME: do not free; the commit is owned by the revpool */
free(commit);
return NULL;
}
int git_commit__parse(git_commit *commit, unsigned int parse_flags, int close_db_object)
{
int error = 0;
if (!commit->odb_open) {
error = git_odb_read(&commit->odb_object, commit->object.pool->db, &commit->object.id);
error = git_odb_read(&commit->odb_object, commit->object.repo->db, &commit->object.id);
if (error < 0)
return error;
......@@ -133,32 +113,9 @@ int git_commit__parse_basic(git_commit *commit)
return 0;
}
git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
git_commit *git_commit_lookup(git_repository *repo, const git_oid *id)
{
git_commit *commit = NULL;
if (pool == NULL)
return NULL;
commit = (git_commit *)git_revpool_table_lookup(pool->objects, id);
if (commit != NULL)
return commit;
commit = git__malloc(sizeof(git_commit));
if (commit == NULL)
return NULL;
memset(commit, 0x0, sizeof(git_commit));
/* Initialize parent object */
git_oid_cpy(&commit->object.id, id);
commit->object.pool = pool;
commit->object.type = GIT_OBJ_COMMIT;
git_revpool_table_insert(pool->objects, (git_revpool_object *)commit);
return commit;
return (git_commit *)git_repository_lookup(repo, id, GIT_OBJ_COMMIT);
}
int git__parse_person(git_person *person, char **buffer_out,
......@@ -257,30 +214,31 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigne
return GIT_EOBJCORRUPTED;
if (parse_flags & GIT_COMMIT_TREE)
commit->tree = git_tree_lookup(commit->object.pool, &oid);
commit->tree = git_tree_lookup(commit->object.repo, &oid);
/*
* TODO: commit grafts!
*/
if (parse_flags & GIT_COMMIT_PARENTS)
git_commit_list_clear(&commit->parents, 0);
clear_parents(commit);
while (git__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
git_commit *parent;
git_commit_parents *node;
if ((parse_flags & GIT_COMMIT_PARENTS) == 0)
continue;
if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
if ((parent = git_commit_lookup(commit->object.repo, &oid)) == NULL)
return GIT_ENOTFOUND;
/* Inherit uninteresting flag */
if (commit->uninteresting)
parent->uninteresting = 1;
if (git_commit_list_push_back(&commit->parents, parent) < 0)
if ((node = git__malloc(sizeof(git_commit_parents))) == NULL)
return GIT_ENOMEM;
node->commit = parent;
node->next = commit->parents;
commit->parents = node;
}
if (git__parse_person(&person, &buffer, buffer_end, "author ") < 0)
......@@ -391,200 +349,3 @@ const char *git_commit_message_short(git_commit *commit)
git_commit__parse(commit, GIT_COMMIT_MESSAGE_SHORT, 0);
return commit->message_short;
}
int git_commit_list_push_back(git_commit_list *list, git_commit *commit)
{
git_commit_node *node = NULL;
node = git__malloc(sizeof(git_commit_list));
if (node == NULL)
return GIT_ENOMEM;
node->commit = commit;
node->next = NULL;
node->prev = list->tail;
if (list->tail == NULL) {
list->head = list->tail = node;
} else {
list->tail->next = node;
list->tail = node;
}
list->size++;
return 0;
}
int git_commit_list_push_front(git_commit_list *list, git_commit *commit)
{
git_commit_node *node = NULL;
node = git__malloc(sizeof(git_commit_list));
if (node == NULL)
return GIT_ENOMEM;
node->commit = commit;
node->next = list->head;
node->prev = NULL;
if (list->head == NULL) {
list->head = list->tail = node;
} else {
list->head->prev = node;
list->head = node;
}
list->size++;
return 0;
}
git_commit *git_commit_list_pop_back(git_commit_list *list)
{
git_commit_node *node;
git_commit *commit;
if (list->tail == NULL)
return NULL;
node = list->tail;
list->tail = list->tail->prev;
if (list->tail == NULL)
list->head = NULL;
commit = node->commit;
free(node);
list->size--;
return commit;
}
git_commit *git_commit_list_pop_front(git_commit_list *list)
{
git_commit_node *node;
git_commit *commit;
if (list->head == NULL)
return NULL;
node = list->head;
list->head = list->head->next;
if (list->head == NULL)
list->tail = NULL;
commit = node->commit;
free(node);
list->size--;
return commit;
}
void git_commit_list_clear(git_commit_list *list, int free_commits)
{
git_commit_node *node, *next_node;
node = list->head;
while (node) {
if (free_commits)
free(node->commit);
next_node = node->next;
free(node);
node = next_node;
}
list->head = list->tail = NULL;
list->size = 0;
}
void git_commit_list_timesort(git_commit_list *list)
{
git_commit_node *p, *q, *e;
int in_size, p_size, q_size, merge_count, i;
if (list->head == NULL)
return;
in_size = 1;
do {
p = list->head;
list->tail = NULL;
merge_count = 0;
while (p != NULL) {
merge_count++;
q = p;
p_size = 0;
q_size = in_size;
for (i = 0; i < in_size && q; ++i, q = q->next)
p_size++;
while (p_size > 0 || (q_size > 0 && q)) {
if (p_size == 0)
e = q, q = q->next, q_size--;
else if (q_size == 0 || q == NULL ||
p->commit->commit_time >= q->commit->commit_time)
e = p, p = p->next, p_size--;
else
e = q, q = q->next, q_size--;
if (list->tail != NULL)
list->tail->next = e;
else
list->head = e;
e->prev = list->tail;
list->tail = e;
}
p = q;
}
list->tail->next = NULL;
in_size *= 2;
} while (merge_count > 1);
}
void git_commit_list_toposort(git_commit_list *list)
{
git_commit *commit;
git_commit_list topo;
memset(&topo, 0x0, sizeof(git_commit_list));
while ((commit = git_commit_list_pop_back(list)) != NULL) {
git_commit_node *p;
if (commit->in_degree > 0) {
commit->topo_delay = 1;
continue;
}
for (p = commit->parents.head; p != NULL; p = p->next) {
p->commit->in_degree--;
if (p->commit->in_degree == 0 && p->commit->topo_delay) {
p->commit->topo_delay = 0;
git_commit_list_push_back(list, p->commit);
}
}
git_commit_list_push_back(&topo, commit);
}
list->head = topo.head;
list->tail = topo.tail;
list->size = topo.size;
}
......@@ -3,26 +3,10 @@
#include "git/commit.h"
#include "tree.h"
#include "revobject.h"
#include "repository.h"
#include <time.h>
struct git_commit_node {
struct git_commit *commit;
struct git_commit_node *next;
struct git_commit_node *prev;
};
struct git_commit_list {
struct git_commit_node *head;
struct git_commit_node *tail;
size_t size;
};
typedef struct git_commit_list git_commit_list;
typedef struct git_commit_node git_commit_node;
#define GIT_COMMIT_TREE (1 << 1)
#define GIT_COMMIT_PARENTS (1 << 2)
#define GIT_COMMIT_AUTHOR (1 << 3)
......@@ -32,12 +16,17 @@ typedef struct git_commit_node git_commit_node;
#define GIT_COMMIT_MESSAGE_SHORT (1 << 7)
#define GIT_COMMIT_FOOTERS (1 << 8)
typedef struct git_commit_parents {
git_commit *commit;
struct git_commit_parents *next;
} git_commit_parents;
struct git_commit {
git_revpool_object object;
git_repository_object object;
git_obj odb_object;
time_t commit_time;
git_commit_list parents;
git_commit_parents *parents;
git_tree *tree;
git_person *author;
......@@ -46,13 +35,8 @@ struct git_commit {
char *message;
char *message_short;
unsigned short in_degree;
unsigned basic_parse:1,
odb_open:1,
seen:1,
uninteresting:1,
topo_delay:1,
flags:25;
odb_open:1;
};
void git_commit__free(git_commit *c);
......@@ -64,15 +48,5 @@ void git_commit__mark_uninteresting(git_commit *commit);
int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
int git__parse_person(git_person *person, char **buffer_out, const char *buffer_end, const char *header);
int git_commit_list_push_back(git_commit_list *list, git_commit *commit);
int git_commit_list_push_front(git_commit_list *list, git_commit *commit);
git_commit *git_commit_list_pop_back(git_commit_list *list);
git_commit *git_commit_list_pop_front(git_commit_list *list);
void git_commit_list_clear(git_commit_list *list, int free_commits);
void git_commit_list_timesort(git_commit_list *list);
void git_commit_list_toposort(git_commit_list *list);
#endif
......@@ -4,6 +4,7 @@
#include "common.h"
#include "oid.h"
#include "tree.h"
#include "repository.h"
/**
* @file git/commit.h
......@@ -18,31 +19,16 @@ GIT_BEGIN_DECL
typedef struct git_commit git_commit;
/**
* Locate a reference to a commit without loading it.
* Lookup a commit object from a repository.
* The generated commit object is owned by the revision
* pool and shall not be freed by the user.
* repo and shall not be freed by the user.
*
* @param pool the pool to use when locating the commit.
* @param repo the repo to use when locating the commit.
* @param id identity of the commit to locate. If the object is
* an annotated tag it will be peeled back to the commit.
* @return the commit; NULL if the commit could not be created
*/
GIT_EXTERN(git_commit *) git_commit_lookup(git_revpool *pool, const git_oid *id);
/**
* Locate a reference to a commit, and try to load and parse it it from
* the commit cache or the object database.
* The generated commit object is owned by the revision
* pool and shall not be freed by the user.
*
* @param pool the pool to use when parsing/caching the commit.
* @param id identity of the commit to locate. If the object is
* an annotated tag it will be peeled back to the commit.
* @return the commit; NULL if the commit does not exist in the
* pool's git_odb, or if the commit is present but is
* too malformed to be parsed successfully.
*/
GIT_EXTERN(git_commit *) git_commit_parse(git_revpool *pool, const git_oid *id);
GIT_EXTERN(git_commit *) git_commit_lookup(git_repository *repo, const git_oid *id);
/**
* Get the id of a commit.
......
......@@ -85,8 +85,14 @@
GIT_BEGIN_DECL
/** A revision traversal pool. */
typedef struct git_revpool git_revpool;
/**
* Representation of an existing git repository,
* including all its object contents
*/
typedef struct git_repository git_repository;
/* Representation of a generic object in a repository */
typedef struct git_repository_object git_repository_object;
/** Parsed representation of a person */
typedef struct git_person {
......
......@@ -35,6 +35,7 @@ GIT_EXTERN(void) git_odb_close(git_odb *db);
/** Basic type (loose or packed) of any Git object. */
typedef enum {
GIT_OBJ_ANY = -2, /**< Object can be any of the following */
GIT_OBJ_BAD = -1, /**< Object is invalid. */
GIT_OBJ__EXT1 = 0, /**< Reserved for future use. */
GIT_OBJ_COMMIT = 1, /**< A commit object. */
......
#ifndef INCLUDE_git_repository_h__
#define INCLUDE_git_repository_h__
#include "common.h"
#include "odb.h"
#include "commit.h"
/**
* @file git/repository.h
* @brief Git revision object management routines
* @defgroup git_repository Git revision object management routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Allocate a new repository object.
*
* TODO: specify the repository's path instead
* of its object database
*
* @param odb an existing object database to back the repo
* @return the new repository handle; NULL on error
*/
GIT_EXTERN(git_repository *) git_repository_alloc(git_odb *odb);
/**
* Lookup a reference to one of the objects in the repostory.
*
* The generated reference is owned by the repository and
* should not be freed by the user.
* The generated reference should be cast back to the
* expected type; e.g.
*
* git_commit *c = (git_commit *)
* git_repository_lookup(repo, id, GIT_OBJ_COMMIT);
*
* The 'type' parameter must match the type of the object
* in the odb; the method will fail otherwise.
* The special value 'GIT_OBJ_ANY' may be passed to let
* the method guess the object's type.
*
* @param repo the repository to look up the object
* @param id the unique identifier for the object
* @param type the type of the object
* @return a reference to the object
*/
GIT_EXTERN(git_repository_object *) git_repository_lookup(git_repository *repo, const git_oid *id, git_otype type);
/**
* Free a previously allocated repository
* @param repo repository handle to close. If NULL nothing occurs.
*/
GIT_EXTERN(void) git_repository_free(git_repository *repo);
/** @} */
GIT_END_DECL
#endif
......@@ -15,87 +15,87 @@
GIT_BEGIN_DECL
/**
* Sort the revpool contents in no particular ordering;
* Sort the repository contents in no particular ordering;
* this sorting is arbritary, implementation-specific
* and subject to change at any time.
* This is the default sorting for new revision pools.
* This is the default sorting for new walkers.
*/
#define GIT_RPSORT_NONE (0)
#define GIT_SORT_NONE (0)
/**
* Sort the revpool contents in topological order
* Sort the repository contents in topological order
* (parents before children); this sorting mode
* can be combined with time sorting.
*/
#define GIT_RPSORT_TOPOLOGICAL (1 << 0)
#define GIT_SORT_TOPOLOGICAL (1 << 0)
/**
* Sort the revpool contents by commit time;
* Sort the repository contents by commit time;
* this sorting mode can be combined with
* topological sorting.
*/
#define GIT_RPSORT_TIME (1 << 1)
#define GIT_SORT_TIME (1 << 1)
/**
* Iterate through the revpool contents in reverse
* Iterate through the repository contents in reverse
* order; this sorting mode can be combined with
* any of the above.
*/
#define GIT_RPSORT_REVERSE (1 << 2)
#define GIT_SORT_REVERSE (1 << 2)
typedef struct git_revwalk git_revwalk;
/**
* Allocate a new revision traversal pool.
*
* The configuration is copied during allocation. Changes
* to the configuration after allocation do not affect the pool
* returned by this function. Callers may safely free the
* passed configuration after the function completes.
* Allocate a new revision walker to iterate through a repo.
*
* @param db the database objects are read from.
* @return the new traversal handle; NULL if memory is exhausted.
* @param repo the repo to walk through
* @return the new walker handle; NULL if memory is exhausted.
*/
GIT_EXTERN(git_revpool *) gitrp_alloc(git_odb *db);
GIT_EXTERN(git_revwalk *) git_revwalk_alloc(git_repository *repo);
/**
* Reset the traversal machinary for reuse.
* @param pool traversal handle to reset.
* Reset the walking machinary for reuse.
* @param walker handle to reset.
*/
GIT_EXTERN(void) gitrp_reset(git_revpool *pool);
GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker);
/**
* Mark an object to start traversal from.
* @param pool the pool being used for the traversal.
* Mark a commit to start traversal from.
* The commit object must belong to the repo which is being walked through.
*
* @param walker the walker being used for the traversal.
* @param commit the commit to start from.
*/
GIT_EXTERN(int) gitrp_push(git_revpool *pool, git_commit *commit);
GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, git_commit *commit);
/**
* Mark a commit (and its ancestors) uninteresting for the output.
* @param pool the pool being used for the traversal.
* @param walker the walker being used for the traversal.
* @param commit the commit that will be ignored during the traversal
*/
GIT_EXTERN(int) gitrp_hide(git_revpool *pool, git_commit *commit);
GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, git_commit *commit);
/**
* Get the next commit from the revision traversal.
* @param pool the pool to pop the commit from.
* @param walk the walker to pop the commit from.
* @return next commit; NULL if there is no more output.
*/
GIT_EXTERN(git_commit *) gitrp_next(git_revpool *pool);
GIT_EXTERN(git_commit *) git_revwalk_next(git_revwalk *walk);
/**
* Change the sorting mode when iterating through the
* revision pool's contents.
* @param pool the pool being used for the traversal.
* repository's contents.
* Changing the sorting mode resets the walker.
* @param walk the walker being used for the traversal.
* @param sort_mode combination of GIT_RPSORT_XXX flags
*/
GIT_EXTERN(void) gitrp_sorting(git_revpool *pool, unsigned int sort_mode);
GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
/**
* Free a revwalk previously allocated.
* @param pool traversal handle to close. If NULL nothing occurs.
* @param walk traversal handle to close. If NULL nothing occurs.
*/
GIT_EXTERN(void) gitrp_free(git_revpool *pool);
GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk);
/** @} */
GIT_END_DECL
......
......@@ -4,6 +4,7 @@
#include "common.h"
#include "oid.h"
#include "tree.h"
#include "repository.h"
/**
* @file git/tag.h
......@@ -18,29 +19,15 @@ GIT_BEGIN_DECL
typedef struct git_tag git_tag;
/**
* Locate a reference to a tag without loading it.
* Lookup a tag object from the repository.
* The generated tag object is owned by the revision
* pool and shall not be freed by the user.
* repo and shall not be freed by the user.
*
* @param pool the pool to use when locating the tag.
* @param repo the repo to use when locating the tag.
* @param id identity of the tag to locate.
* @return the tag; NULL if the tag could not be created
*/
GIT_EXTERN(git_tag *) git_tag_lookup(git_revpool *pool, const git_oid *id);
/**
* Locate a reference to a tag, and try to load and parse it it from
* the object cache or the object database.
* The generated tag object is owned by the revision
* pool and shall not be freed by the user.
*
* @param pool the pool to use when parsing/caching the tag.
* @param id identity of the tag to locate.
* @return the tag; NULL if the tag does not exist in the
* pool's git_odb, or if the tag is present but is
* too malformed to be parsed successfully.
*/
GIT_EXTERN(git_tag *) git_tag_parse(git_revpool *pool, const git_oid *id);
GIT_EXTERN(git_tag *) git_tag_lookup(git_repository *repo, const git_oid *id);
/**
* Get the id of a tag.
......
......@@ -3,6 +3,7 @@
#include "common.h"
#include "oid.h"
#include "repository.h"
/**
* @file git/tree.h
......@@ -17,27 +18,15 @@ GIT_BEGIN_DECL
typedef struct git_tree git_tree;
/**
* Locate a reference to a tree without loading it.
* Lookup a tree object from the repository.
* The generated tree object is owned by the revision
* pool and shall not be freed by the user.
* repo and shall not be freed by the user.
*
* @param pool the pool to use when locating the tree.
* @param repo the repo to use when locating the tree.
* @param id identity of the tree to locate.
* @return the tree; NULL if the tree could not be created
*/
GIT_EXTERN(git_tree *) git_tree_lookup(git_revpool *pool, const git_oid *id);
/**
* Locate a reference to a tree object and parse its
* contents.
* The generated tree object is owned by the revision
* pool and shall not be freed by the user.
*
* @param pool the pool to use when locating the tree.
* @param id identity of the tree to locate.
* @return the tree; NULL if the tree could not be created
*/
GIT_EXTERN(git_tree *) git_tree_parse(git_revpool *pool, const git_oid *id);
GIT_EXTERN(git_tree *) git_tree_lookup(git_repository *repo, const git_oid *id);
/**
* Get the id of a tree.
......
......@@ -24,25 +24,55 @@
*/
#include "common.h"
#include "revobject.h"
#include "repository.h"
#include "commit.h"
static const int default_table_size = 32;
static const double max_load_factor = 0.65;
static unsigned int git_revpool_table__hash(const git_oid *id)
static void hashtable_resize(git_hashtable *table)
{
unsigned int r;
memcpy(&r, id->id, sizeof(r));
return r;
git_hashtable_node **new_nodes;
unsigned int new_size, i;
assert(table);
new_size = (table->size_mask + 1) * 2;
new_nodes = git__malloc(new_size * sizeof(git_hashtable_node *));
if (new_nodes == NULL)
return;
memset(new_nodes, 0x0, new_size * sizeof(git_hashtable_node *));
for (i = 0; i <= table->size_mask; ++i) {
git_hashtable_node *n;
unsigned int index;
while ((n = table->nodes[i]) != NULL) {
table->nodes[i] = n->next;
index = n->hash & (new_size - 1);
n->next = new_nodes[index];
new_nodes[index] = n;
}
}
free(table->nodes);
table->nodes = new_nodes;
table->size_mask = (new_size - 1);
table->max_count = (unsigned int)(new_size * max_load_factor);
}
git_revpool_table *git_revpool_table_create(unsigned int min_size)
git_hashtable *git_hashtable_alloc(unsigned int min_size,
git_hash_ptr hash,
git_hash_keyeq_ptr key_eq)
{
git_revpool_table *table;
unsigned int i;
git_hashtable *table;
table = git__malloc(sizeof(*table));
assert(hash && key_eq);
if (table == NULL)
if ((table = git__malloc(sizeof(git_hashtable))) == NULL)
return NULL;
/* round up size to closest power of 2 */
......@@ -57,7 +87,10 @@ git_revpool_table *git_revpool_table_create(unsigned int min_size)
table->count = 0;
table->max_count = (unsigned int)((min_size + 1) * max_load_factor);
table->nodes = git__malloc((min_size + 1) * sizeof(git_revpool_node *));
table->hash = hash;
table->key_equal = key_eq;
table->nodes = git__malloc((min_size + 1) * sizeof(git_hashtable_node *));
if (table->nodes == NULL) {
free(table);
......@@ -70,48 +103,78 @@ git_revpool_table *git_revpool_table_create(unsigned int min_size)
return table;
}
int git_revpool_table_insert(git_revpool_table *table, git_revpool_object *object)
void git_hashtable_clear(git_hashtable *table)
{
git_revpool_node *node;
unsigned int index, hash;
unsigned int index;
assert(table);
for (index = 0; index <= table->size_mask; ++index) {
git_hashtable_node *node, *next_node;
node = table->nodes[index];
while (node != NULL) {
next_node = node->next;
free(node);
node = next_node;
}
table->nodes[index] = NULL;
}
table->count = 0;
}
void git_hashtable_free(git_hashtable *table)
{
assert(table);
if (table == NULL)
return GIT_ERROR;
git_hashtable_clear(table);
free(table->nodes);
free(table);
}
int git_hashtable_insert(git_hashtable *table, const void *key, void *value)
{
git_hashtable_node *node;
uint32_t index, hash;
assert(table);
if (table->count + 1 > table->max_count)
git_revpool_table_resize(table);
hashtable_resize(table);
node = git__malloc(sizeof(git_revpool_node));
node = git__malloc(sizeof(git_hashtable_node));
if (node == NULL)
return GIT_ENOMEM;
hash = git_revpool_table__hash(&object->id);
hash = table->hash(key);
index = (hash & table->size_mask);
node->object = object;
node->object = value;
node->hash = hash;
node->next = table->nodes[index];
table->nodes[index] = node;
table->count++;
return 0;
return GIT_SUCCESS;
}
git_revpool_object *git_revpool_table_lookup(git_revpool_table *table, const git_oid *id)
void *git_hashtable_lookup(git_hashtable *table, const void *key)
{
git_revpool_node *node;
unsigned int index, hash;
git_hashtable_node *node;
uint32_t index, hash;
if (table == NULL)
return NULL;
assert(table);
hash = git_revpool_table__hash(id);
hash = table->hash(key);
index = (hash & table->size_mask);
node = table->nodes[index];
while (node != NULL) {
if (node->hash == hash && git_oid_cmp(id, &node->object->id) == 0)
if (node->hash == hash && table->key_equal(node->object, key))
return node->object;
node = node->next;
......@@ -120,60 +183,13 @@ git_revpool_object *git_revpool_table_lookup(git_revpool_table *table, const git
return NULL;
}
void git_revpool_table_resize(git_revpool_table *table)
{
git_revpool_node **new_nodes;
unsigned int new_size, i;
new_size = (table->size_mask + 1) * 2;
new_nodes = git__malloc(new_size * sizeof(git_revpool_node *));
if (new_nodes == NULL)
return;
memset(new_nodes, 0x0, new_size * sizeof(git_revpool_node *));
for (i = 0; i <= table->size_mask; ++i) {
git_revpool_node *n;
unsigned int index;
while ((n = table->nodes[i]) != NULL) {
table->nodes[i] = n->next;
index = n->hash & (new_size - 1);
n->next = new_nodes[index];
new_nodes[index] = n;
}
}
free(table->nodes);
table->nodes = new_nodes;
table->size_mask = (new_size - 1);
table->max_count = (unsigned int)(new_size * max_load_factor);
}
void git_revpool_table_free(git_revpool_table *table)
void git_hashtable_iterator_init(git_hashtable *table, git_hashtable_iterator *it)
{
unsigned int index;
for (index = 0; index <= table->size_mask; ++index) {
git_revpool_node *node, *next_node;
node = table->nodes[index];
while (node != NULL) {
next_node = node->next;
free(node);
node = next_node;
}
}
free(table->nodes);
free(table);
}
assert(table && it);
void git_revpool_tableit_init(git_revpool_table *table, git_revpool_tableit *it)
{
memset(it, 0x0, sizeof(git_revpool_tableit));
memset(it, 0x0, sizeof(git_hashtable_iterator));
it->nodes = table->nodes;
it->current_node = NULL;
......@@ -181,9 +197,11 @@ void git_revpool_tableit_init(git_revpool_table *table, git_revpool_tableit *it)
it->size = table->size_mask + 1;
}
git_revpool_object *git_revpool_tableit_next(git_revpool_tableit *it)
void *git_hashtable_iterator_next(git_hashtable_iterator *it)
{
git_revpool_node *next = NULL;
git_hashtable_node *next = NULL;
assert(it);
while (it->current_node == NULL) {
if (it->current_pos >= it->size)
......@@ -198,13 +216,3 @@ git_revpool_object *git_revpool_tableit_next(git_revpool_tableit *it)
return next->object;
}
git_revpool_object *git_revpool_tableit_nextfilter(git_revpool_tableit *it, git_otype type)
{
git_revpool_object *obj;
do {
obj = git_revpool_tableit_next(it);
} while (obj != NULL && obj->type != type);
return obj;
}
#ifndef INCLUDE_hashtable_h__
#define INCLUDE_hashtable_h__
#include "git/common.h"
#include "git/oid.h"
#include "git/odb.h"
typedef uint32_t (*git_hash_ptr)(const void *);
typedef int (*git_hash_keyeq_ptr)(void *obj, const void *obj_key);
struct git_hashtable_node {
void *object;
uint32_t hash;
struct git_hashtable_node *next;
};
struct git_hashtable {
struct git_hashtable_node **nodes;
unsigned int size_mask;
unsigned int count;
unsigned int max_count;
git_hash_ptr hash;
git_hash_keyeq_ptr key_equal;
};
struct git_hashtable_iterator {
struct git_hashtable_node **nodes;
struct git_hashtable_node *current_node;
unsigned int current_pos;
unsigned int size;
};
typedef struct git_hashtable_node git_hashtable_node;
typedef struct git_hashtable git_hashtable;
typedef struct git_hashtable_iterator git_hashtable_iterator;
git_hashtable *git_hashtable_alloc(unsigned int min_size,
git_hash_ptr hash,
git_hash_keyeq_ptr key_eq);
int git_hashtable_insert(git_hashtable *h, const void *key, void *value);
void *git_hashtable_lookup(git_hashtable *h, const void *key);
void git_hashtable_free(git_hashtable *h);
void git_hashtable_clear(git_hashtable *h);
void *git_hashtable_iterator_next(git_hashtable_iterator *it);
void git_hashtable_iterator_init(git_hashtable *h, git_hashtable_iterator *it);
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "common.h"
#include "repository.h"
#include "commit.h"
#include "tag.h"
static const int default_table_size = 32;
static const double max_load_factor = 0.65;
uint32_t git_repository_object_hash(const void *key)
{
uint32_t r;
git_oid *id;
id = (git_oid *)key;
memcpy(&r, id->id, sizeof(r));
return r;
}
int git_repository_object_haskey(void *object, const void *key)
{
git_repository_object *obj;
git_oid *oid;
obj = (git_repository_object *)object;
oid = (git_oid *)key;
return (git_oid_cmp(oid, &obj->id) == 0);
}
git_repository *git_repository_alloc(git_odb *odb)
{
git_repository *repo = git__malloc(sizeof(git_repository));
if (!repo)
return NULL;
memset(repo, 0x0, sizeof(git_repository));
repo->objects = git_hashtable_alloc(
default_table_size,
git_repository_object_hash,
git_repository_object_haskey);
if (repo->objects == NULL) {
free(repo);
return NULL;
}
repo->db = odb; /* TODO: create ODB manually! */
return repo;
}
void git_repository_free(git_repository *repo)
{
git_hashtable_iterator it;
git_repository_object *object;
git_hashtable_iterator_init(repo->objects, &it);
while ((object = (git_repository_object *)
git_hashtable_iterator_next(&it)) != NULL) {
switch (object->type) {
case GIT_OBJ_COMMIT:
git_commit__free((git_commit *)object);
break;
case GIT_OBJ_TREE:
git_tree__free((git_tree *)object);
break;
case GIT_OBJ_TAG:
git_tag__free((git_tag *)object);
break;
default:
free(object);
break;
}
}
git_hashtable_free(repo->objects);
/* TODO: free odb */
free(repo);
}
git_repository_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_otype type)
{
git_repository_object *object = NULL;
git_obj obj_file;
assert(repo);
object = git_hashtable_lookup(repo->objects, id);
if (object != NULL)
return object;
if (git_odb_read(&obj_file, repo->db, id) < 0 ||
(type != GIT_OBJ_ANY && type != obj_file.type))
return NULL;
object = git__malloc(sizeof(git_commit));
if (object == NULL)
return NULL;
memset(object, 0x0, sizeof(git_commit));
/* Initialize parent object */
git_oid_cpy(&object->id, id);
object->repo = repo;
object->type = obj_file.type;
git_hashtable_insert(repo->objects, &object->id, object);
git_obj_close(&obj_file);
return object;
}
#ifndef INCLUDE_repository_h__
#define INCLUDE_repository_h__
#include "git/common.h"
#include "git/oid.h"
#include "git/odb.h"
#include "git/repository.h"
#include "hashtable.h"
struct git_repository_object {
git_oid id;
git_repository *repo;
git_otype type;
};
struct git_repository {
git_odb *db;
git_hashtable *objects;
};
int git_repository__insert(git_repository *repo, git_repository_object *obj);
git_repository_object *git_repository__lookup(git_repository *repo, const git_oid *id);
#endif
#ifndef INCLUDE_objecttable_h__
#define INCLUDE_objecttable_h__
#include "git/common.h"
#include "git/oid.h"
#include "git/odb.h"
struct git_revpool_object {
git_oid id;
git_revpool *pool;
git_otype type;
};
struct git_revpool_node {
struct git_revpool_object *object;
unsigned int hash;
struct git_revpool_node *next;
};
struct git_revpool_table {
struct git_revpool_node **nodes;
unsigned int size_mask;
unsigned int count;
unsigned int max_count;
};
struct git_revpool_tableit {
struct git_revpool_node **nodes;
struct git_revpool_node *current_node;
unsigned int current_pos;
unsigned int size;
};
typedef struct git_revpool_node git_revpool_node;
typedef struct git_revpool_object git_revpool_object;
typedef struct git_revpool_table git_revpool_table;
typedef struct git_revpool_tableit git_revpool_tableit;
git_revpool_table *git_revpool_table_create(unsigned int min_size);
int git_revpool_table_insert(git_revpool_table *table, git_revpool_object *object);
git_revpool_object *git_revpool_table_lookup(git_revpool_table *table, const git_oid *id);
void git_revpool_table_resize(git_revpool_table *table);
void git_revpool_table_free(git_revpool_table *table);
git_revpool_object *git_revpool_tableit_next(git_revpool_tableit *it);
git_revpool_object *git_revpool_tableit_nextfilter(git_revpool_tableit *it, git_otype type);
void git_revpool_tableit_init(git_revpool_table *table, git_revpool_tableit *it);
#endif
......@@ -5,21 +5,63 @@
#include "git/revwalk.h"
#include "commit.h"
#include "repository.h"
#include "hashtable.h"
struct git_revpool {
git_odb *db;
struct git_revwalk_commit;
git_commit_list iterator;
git_commit *(*next_commit)(git_commit_list *);
typedef struct git_revwalk_listnode {
struct git_revwalk_commit *walk_commit;
struct git_revwalk_listnode *next;
struct git_revwalk_listnode *prev;
} git_revwalk_listnode;
git_commit_list roots;
git_revpool_table *objects;
typedef struct git_revwalk_list {
struct git_revwalk_listnode *head;
struct git_revwalk_listnode *tail;
size_t size;
} git_revwalk_list;
struct git_revwalk_commit {
git_commit *commit_object;
git_revwalk_list parents;
unsigned short in_degree;
unsigned seen:1,
uninteresting:1,
topo_delay:1,
flags:25;
};
typedef struct git_revwalk_commit git_revwalk_commit;
struct git_revwalk {
git_repository *repo;
git_hashtable *commits;
git_revwalk_list iterator;
git_revwalk_commit *(*next)(git_revwalk_list *);
unsigned walking:1;
unsigned int sorting;
};
void gitrp__prepare_walk(git_revpool *pool);
int gitrp__enroot(git_revpool *pool, git_commit *commit);
void git_revwalk__prepare_walk(git_revwalk *walk);
int git_revwalk__enroot(git_revwalk *walk, git_commit *commit);
int git_revwalk_list_push_back(git_revwalk_list *list, git_revwalk_commit *commit);
int git_revwalk_list_push_front(git_revwalk_list *list, git_revwalk_commit *obj);
git_revwalk_commit *git_revwalk_list_pop_back(git_revwalk_list *list);
git_revwalk_commit *git_revwalk_list_pop_front(git_revwalk_list *list);
void git_revwalk_list_clear(git_revwalk_list *list);
void git_revwalk_list_timesort(git_revwalk_list *list);
void git_revwalk_list_toposort(git_revwalk_list *list);
#endif /* INCLUDE_revwalk_h__ */
......@@ -28,7 +28,7 @@
#include "tag.h"
#include "revwalk.h"
#include "git/odb.h"
#include "git/repository.h"
void git_tag__free(git_tag *tag)
{
......@@ -43,7 +43,7 @@ const git_oid *git_tag_id(git_tag *t)
return &t->object.id;
}
const git_revpool_object *git_tag_target(git_tag *t)
const git_repository_object *git_tag_target(git_tag *t)
{
if (t->target)
return t->target;
......@@ -183,7 +183,7 @@ int git_tag__parse(git_tag *tag)
int error = 0;
git_obj odb_object;
error = git_odb_read(&odb_object, tag->object.pool->db, &tag->object.id);
error = git_odb_read(&odb_object, tag->object.repo->db, &tag->object.id);
if (error < 0)
return error;
......@@ -199,30 +199,7 @@ cleanup:
return error;
}
git_tag *git_tag_lookup(git_revpool *pool, const git_oid *id)
git_tag *git_tag_lookup(git_repository *repo, const git_oid *id)
{
git_tag *tag = NULL;
if (pool == NULL)
return NULL;
tag = (git_tag *)git_revpool_table_lookup(pool->objects, id);
if (tag != NULL)
return tag;
tag = git__malloc(sizeof(git_tag));
if (tag == NULL)
return NULL;
memset(tag, 0x0, sizeof(git_tag));
/* Initialize parent object */
git_oid_cpy(&tag->object.id, id);
tag->object.pool = pool;
tag->object.type = GIT_OBJ_TAG;
git_revpool_table_insert(pool->objects, (git_revpool_object *)tag);
return tag;
return (git_tag *)git_repository_lookup(repo, id, GIT_OBJ_TAG);
}
......@@ -2,12 +2,12 @@
#define INCLUDE_tag_h__
#include "git/tag.h"
#include "revobject.h"
#include "repository.h"
struct git_tag {
git_revpool_object object;
git_repository_object object;
git_revpool_object *target;
git_repository_object *target;
git_otype type;
char *tag_name;
git_person *tagger;
......
......@@ -27,6 +27,7 @@
#include "commit.h"
#include "revwalk.h"
#include "tree.h"
#include "git/repository.h"
void git_tree__free(git_tree *tree)
{
......@@ -38,51 +39,9 @@ const git_oid *git_tree_id(git_tree *tree)
return &tree->object.id;
}
git_tree *git_tree_lookup(git_revpool *pool, const git_oid *id)
git_tree *git_tree_lookup(git_repository *repo, const git_oid *id)
{
git_tree *tree = NULL;
if (pool == NULL)
return NULL;
tree = (git_tree *)git_revpool_table_lookup(pool->objects, id);
if (tree != NULL)
return tree;
tree = git__malloc(sizeof(git_tree));
if (tree == NULL)
return NULL;
memset(tree, 0x0, sizeof(git_tree));
/* Initialize parent object */
git_oid_cpy(&tree->object.id, id);
tree->object.pool = pool;
tree->object.type = GIT_OBJ_TREE;
git_revpool_table_insert(pool->objects, (git_revpool_object *)tree);
return tree;
}
git_tree *git_tree_parse(git_revpool *pool, const git_oid *id)
{
git_tree *tree = NULL;
if ((tree = git_tree_lookup(pool, id)) == NULL)
return NULL;
if (git_tree__parse(tree) < 0)
goto error_cleanup;
return tree;
error_cleanup:
/* FIXME: do not free; the tree is owned by the revpool */
free(tree);
return NULL;
return (git_tree *)git_repository_lookup(repo, id, GIT_OBJ_TREE);
}
int git_tree__parse(git_tree *tree)
......@@ -93,7 +52,7 @@ int git_tree__parse(git_tree *tree)
git_obj odb_object;
char *buffer, *buffer_end;
error = git_odb_read(&odb_object, tree->object.pool->db, &tree->object.id);
error = git_odb_read(&odb_object, tree->object.repo->db, &tree->object.id);
if (error < 0)
return error;
......
......@@ -2,7 +2,7 @@
#define INCLUDE_tree_h__
#include <git/tree.h>
#include "revobject.h"
#include "repository.h"
struct git_tree_entry {
......@@ -16,7 +16,7 @@ struct git_tree_entry {
typedef struct git_tree_entry git_tree_entry;
struct git_tree {
git_revpool_object object;
git_repository_object object;
size_t byte_size;
git_tree_entry *entries;
......
......@@ -72,7 +72,7 @@ a simple commit which works\n",
/* simple commit, 1 parents */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
parent a4a7dce85cf63874e984719f4fdd239f5145052f\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
......@@ -216,17 +216,23 @@ BEGIN_TEST(parse_buffer_test)
const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken);
const int working_commit_count = sizeof(test_commits_working) / sizeof(*test_commits_working);
const unsigned int default_flags = 0xFF; /* parse everything */
const unsigned int default_flags =
GIT_COMMIT_AUTHOR |
GIT_COMMIT_COMMITTER |
GIT_COMMIT_TIME |
GIT_COMMIT_MESSAGE |
GIT_COMMIT_MESSAGE_SHORT; /* parse everything */
int i;
git_repository *repo = git_repository_alloc(NULL);
git_revpool *pool = gitrp_alloc(NULL);
must_be_true(repo != NULL);
for (i = 0; i < broken_commit_count; ++i) {
git_commit *commit;
commit = git__malloc(sizeof(git_commit));
memset(commit, 0x0, sizeof(git_commit));
commit->object.pool = pool;
commit->object.repo = repo;
must_fail(git_commit__parse_buffer(
commit,
......@@ -242,7 +248,7 @@ BEGIN_TEST(parse_buffer_test)
git_commit *commit;
commit = git__malloc(sizeof(git_commit));
memset(commit, 0x0, sizeof(git_commit));
commit->object.pool = pool;
commit->object.repo = repo;
must_pass(git_commit__parse_buffer(
commit,
......@@ -254,6 +260,6 @@ BEGIN_TEST(parse_buffer_test)
git_commit__free(commit);
}
gitrp_free(pool);
git_repository_free(repo);
END_TEST
......@@ -21,12 +21,12 @@ BEGIN_TEST(query_details_test)
unsigned int i;
git_odb *db;
git_revpool *pool;
git_repository *repo;
must_pass(git_odb_open(&db, odb_dir));
pool = gitrp_alloc(db);
must_be_true(pool != NULL);
repo = git_repository_alloc(db);
must_be_true(repo != NULL);
for (i = 0; i < commit_count; ++i) {
git_oid id;
......@@ -38,7 +38,7 @@ BEGIN_TEST(query_details_test)
git_oid_mkstr(&id, commit_ids[i]);
commit = git_commit_parse(pool, &id);
commit = git_commit_lookup(repo, &id);
must_be_true(commit != NULL);
message = git_commit_message(commit);
......@@ -56,6 +56,6 @@ BEGIN_TEST(query_details_test)
must_be_true(commit_time > 0);
}
gitrp_free(pool);
git_repository_free(repo);
git_odb_close(db);
END_TEST
......@@ -29,49 +29,106 @@ static const char *commit_ids[] = {
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
};
static const int commit_sorting_topo[] = {0, 1, 2, 3, 5, 4};
static const int commit_sorting_time[] = {0, 3, 1, 2, 5, 4};
static const int commit_sorting_topo_reverse[] = {4, 5, 3, 2, 1, 0};
static const int commit_sorting_time_reverse[] = {4, 5, 2, 1, 3, 0};
static const int commit_sorting_topo_time[] = {0};
/* Careful: there are two possible topological sorts */
static const int commit_sorting_topo[][6] = {
{0, 1, 2, 3, 5, 4}, {0, 3, 1, 2, 5, 4}
};
static const int commit_sorting_time[][6] = {
{0, 3, 1, 2, 5, 4}
};
static const int commit_sorting_topo_reverse[][6] = {
{4, 5, 3, 2, 1, 0}, {4, 5, 2, 1, 3, 0}
};
static const int commit_sorting_time_reverse[][6] = {
{4, 5, 2, 1, 3, 0}
};
static const int commit_count = 6;
static const int result_bytes = 24;
static int get_commit_index(git_commit *commit)
{
int i;
char oid[40];
git_oid_fmt(oid, &commit->object.id);
for (i = 0; i < commit_count; ++i)
if (memcmp(oid, commit_ids[i], 40) == 0)
return i;
return -1;
}
static int test_walk(git_revwalk *walk, git_commit *start_from,
int flags, const int possible_results[][6], int results_count)
{
git_commit *commit = NULL;
int i;
int result_array[commit_count];
git_revwalk_sorting(walk, flags);
git_revwalk_push(walk, start_from);
for (i = 0; i < commit_count; ++i)
result_array[i] = -1;
i = 0;
while ((commit = git_revwalk_next(walk)) != NULL)
result_array[i++] = get_commit_index(commit);
for (i = 0; i < results_count; ++i)
if (memcmp(possible_results[i],
result_array, result_bytes) == 0)
return GIT_SUCCESS;
return GIT_ERROR;
}
BEGIN_TEST(simple_walk_test)
git_odb *db;
git_oid id;
git_revpool *pool;
git_repository *repo;
git_revwalk *walk;
git_commit *head = NULL;
must_pass(git_odb_open(&db, odb_dir));
pool = gitrp_alloc(db);
must_be_true(pool != NULL);
repo = git_repository_alloc(db);
must_be_true(repo != NULL);
walk = git_revwalk_alloc(repo);
must_be_true(walk != NULL);
git_oid_mkstr(&id, commit_head);
head = git_commit_parse(pool, &id);
head = git_commit_lookup(repo, &id);
must_be_true(head != NULL);
gitrp_push(pool, head);
#define TEST_WALK(sort_flags, result_array) {\
char oid[40]; int i = 0;\
git_commit *commit = NULL;\
gitrp_sorting(pool, sort_flags);\
while ((commit = gitrp_next(pool)) != NULL) {\
git_oid_fmt(oid, &commit->object.id);\
must_be_true(memcmp(oid, commit_ids[result_array[i++]], 40) == 0);\
}\
must_be_true(i == sizeof(result_array)/sizeof(int));\
gitrp_reset(pool);\
}
TEST_WALK(GIT_RPSORT_TIME, commit_sorting_time);
TEST_WALK(GIT_RPSORT_TOPOLOGICAL, commit_sorting_topo);
TEST_WALK(GIT_RPSORT_TIME | GIT_RPSORT_REVERSE, commit_sorting_time_reverse);
TEST_WALK(GIT_RPSORT_TOPOLOGICAL | GIT_RPSORT_REVERSE, commit_sorting_topo_reverse);
must_pass(test_walk(walk, head,
GIT_SORT_TIME,
commit_sorting_time, 1));
must_pass(test_walk(walk, head,
GIT_SORT_TOPOLOGICAL,
commit_sorting_topo, 2));
must_pass(test_walk(walk, head,
GIT_SORT_TIME | GIT_SORT_REVERSE,
commit_sorting_time_reverse, 1));
must_pass(test_walk(walk, head,
GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE,
commit_sorting_topo_reverse, 2));
#undef TEST_WALK
gitrp_free(pool);
git_revwalk_free(walk);
git_repository_free(repo);
git_odb_close(db);
END_TEST
#include "test_lib.h"
#include "test_helpers.h"
#include "commit.h"
#include "revwalk.h"
#include <git/odb.h>
#include <git/commit.h>
BEGIN_TEST(list_timesort_test)
git_commit_list list;
git_commit_node *n;
git_revwalk_list list;
git_revwalk_listnode *n;
int i, t;
time_t previous_time;
#define TEST_SORTED() \
previous_time = INT_MAX;\
for (n = list.head; n != NULL; n = n->next) {\
must_be_true(n->commit->commit_time <= previous_time);\
previous_time = n->commit->commit_time;\
must_be_true(n->walk_commit->commit_object->commit_time <= previous_time);\
previous_time = n->walk_commit->commit_object->commit_time;\
}
memset(&list, 0x0, sizeof(git_commit_list));
#define CLEAR_LIST() \
for (n = list.head; n != NULL; n = n->next) {\
free(n->walk_commit->commit_object);\
free(n->walk_commit);\
}\
git_revwalk_list_clear(&list);
memset(&list, 0x0, sizeof(git_revwalk_list));
srand((unsigned int)time(NULL));
for (t = 0; t < 20; ++t) {
......@@ -27,30 +35,36 @@ BEGIN_TEST(list_timesort_test)
/* Purely random sorting test */
for (i = 0; i < test_size; ++i) {
git_commit *c = git__malloc(sizeof(git_commit));
git_revwalk_commit *rc = git__malloc(sizeof(git_revwalk_commit));
c->commit_time = (time_t)rand();
rc->commit_object = c;
git_commit_list_push_back(&list, c);
git_revwalk_list_push_back(&list, rc);
}
git_commit_list_timesort(&list);
git_revwalk_list_timesort(&list);
TEST_SORTED();
git_commit_list_clear(&list, 1);
CLEAR_LIST();
}
/* Try to sort list with all dates equal. */
for (i = 0; i < 200; ++i) {
git_commit *c = git__malloc(sizeof(git_commit));
git_revwalk_commit *rc = git__malloc(sizeof(git_revwalk_commit));
c->commit_time = 0;
rc->commit_object = c;
git_commit_list_push_back(&list, c);
git_revwalk_list_push_back(&list, rc);
}
git_commit_list_timesort(&list);
git_revwalk_list_timesort(&list);
TEST_SORTED();
git_commit_list_clear(&list, 1);
CLEAR_LIST();
/* Try to sort empty list */
git_commit_list_timesort(&list);
git_revwalk_list_timesort(&list);
TEST_SORTED();
END_TEST
#include "test_lib.h"
#include "test_helpers.h"
#include "commit.h"
#include "revobject.h"
#include "hash.h"
typedef struct _aux_object {
git_revpool_object object;
int visited;
} aux_object;
BEGIN_TEST(table_iterator)
const int objects_n = 32;
int i;
aux_object *objects, *ob;
git_revpool_table *table = NULL;
git_revpool_tableit iterator;
table = git_revpool_table_create(objects_n * 2);
must_be_true(table != NULL);
objects = git__malloc(objects_n * sizeof(aux_object));
memset(objects, 0x0, objects_n * sizeof(aux_object));
/* populate the hash table */
for (i = 0; i < objects_n; ++i) {
git_hash_buf(&(objects[i].object.id), &i, sizeof(int));
must_pass(git_revpool_table_insert(table, (git_revpool_object *)&(objects[i])));
}
git_revpool_tableit_init(table, &iterator);
/* iterate through all nodes, mark as visited */
while ((ob = (aux_object *)git_revpool_tableit_next(&iterator)) != NULL)
ob->visited = 1;
/* make sure all nodes have been visited */
for (i = 0; i < objects_n; ++i)
must_be_true(objects[i].visited);
git_revpool_table_free(table);
free(objects);
END_TEST
#include "test_lib.h"
#include "test_helpers.h"
#include "commit.h"
#include "revobject.h"
#include "hash.h"
#include "hashtable.h"
typedef struct table_item
{
int __bulk;
git_oid id;
} table_item;
uint32_t hash_func(const void *key)
{
uint32_t r;
git_oid *id;
id = (git_oid *)key;
memcpy(&r, id->id, sizeof(r));
return r;
}
int hash_haskey(void *item, const void *key)
{
table_item *obj;
git_oid *oid;
obj = (table_item *)item;
oid = (git_oid *)key;
return (git_oid_cmp(oid, &obj->id) == 0);
}
BEGIN_TEST(table_create)
git_revpool_table *table = NULL;
git_hashtable *table = NULL;
table = git_revpool_table_create(55);
table = git_hashtable_alloc(55, hash_func, hash_haskey);
must_be_true(table != NULL);
must_be_true(table->size_mask + 1 == 64);
git_revpool_table_free(table);
git_hashtable_free(table);
END_TEST
......@@ -21,28 +48,29 @@ BEGIN_TEST(table_populate)
const int objects_n = 32;
int i;
git_revpool_object *objects;
git_revpool_table *table = NULL;
table = git_revpool_table_create(objects_n * 2);
table_item *objects;
git_hashtable *table = NULL;
table = git_hashtable_alloc(objects_n * 2, hash_func, hash_haskey);
must_be_true(table != NULL);
objects = git__malloc(objects_n * sizeof(git_revpool_object));
memset(objects, 0x0, objects_n * sizeof(git_revpool_object));
objects = git__malloc(objects_n * sizeof(table_item));
memset(objects, 0x0, objects_n * sizeof(table_item));
/* populate the hash table */
for (i = 0; i < objects_n; ++i) {
git_hash_buf(&(objects[i].id), &i, sizeof(int));
must_pass(git_revpool_table_insert(table, &(objects[i])));
must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
}
/* make sure all the inserted objects can be found */
for (i = 0; i < objects_n; ++i) {
git_oid id;
git_revpool_object *ob;
table_item *ob;
git_hash_buf(&id, &i, sizeof(int));
ob = git_revpool_table_lookup(table, &id);
ob = (table_item *)git_hashtable_lookup(table, &id);
must_be_true(ob != NULL);
must_be_true(ob == &(objects[i]));
......@@ -55,10 +83,10 @@ BEGIN_TEST(table_populate)
hash_id = (rand() % 50000) + objects_n;
git_hash_buf(&id, &hash_id, sizeof(int));
must_be_true(git_revpool_table_lookup(table, &id) == NULL);
must_be_true(git_hashtable_lookup(table, &id) == NULL);
}
git_revpool_table_free(table);
git_hashtable_free(table);
free(objects);
END_TEST
......@@ -69,55 +97,38 @@ BEGIN_TEST(table_resize)
const int objects_n = 64;
int i;
unsigned int old_size;
git_revpool_object *objects;
git_revpool_table *table = NULL;
table_item *objects;
git_hashtable *table = NULL;
table = git_revpool_table_create(objects_n);
table = git_hashtable_alloc(objects_n, hash_func, hash_haskey);
must_be_true(table != NULL);
objects = git__malloc(objects_n * sizeof(git_revpool_object));
memset(objects, 0x0, objects_n * sizeof(git_revpool_object));
objects = git__malloc(objects_n * sizeof(table_item));
memset(objects, 0x0, objects_n * sizeof(table_item));
old_size = table->size_mask + 1;
/* populate the hash table -- should be automatically resized */
for (i = 0; i < objects_n; ++i) {
git_hash_buf(&(objects[i].id), &i, sizeof(int));
must_pass(git_revpool_table_insert(table, &(objects[i])));
}
must_be_true(table->size_mask > old_size);
/* make sure all the inserted objects can be found */
for (i = 0; i < objects_n; ++i) {
git_oid id;
git_revpool_object *ob;
git_hash_buf(&id, &i, sizeof(int));
ob = git_revpool_table_lookup(table, &id);
must_be_true(ob != NULL);
must_be_true(ob == &(objects[i]));
must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
}
/* force another resize */
old_size = table->size_mask + 1;
git_revpool_table_resize(table);
must_be_true(table->size_mask > old_size);
/* make sure all the inserted objects can be found */
for (i = 0; i < objects_n; ++i) {
git_oid id;
git_revpool_object *ob;
table_item *ob;
git_hash_buf(&id, &i, sizeof(int));
ob = git_revpool_table_lookup(table, &id);
ob = (table_item *)git_hashtable_lookup(table, &id);
must_be_true(ob != NULL);
must_be_true(ob == &(objects[i]));
}
git_revpool_table_free(table);
git_hashtable_free(table);
free(objects);
END_TEST
#include "test_lib.h"
#include "test_helpers.h"
#include "commit.h"
#include "hashtable.h"
#include "hash.h"
typedef struct _aux_object {
int __bulk;
git_oid id;
int visited;
} table_item;
uint32_t hash_func(const void *key)
{
uint32_t r;
git_oid *id;
id = (git_oid *)key;
memcpy(&r, id->id, sizeof(r));
return r;
}
int hash_haskey(void *item, const void *key)
{
table_item *obj;
git_oid *oid;
obj = (table_item *)item;
oid = (git_oid *)key;
return (git_oid_cmp(oid, &obj->id) == 0);
}
BEGIN_TEST(table_iterator)
const int objects_n = 32;
int i;
table_item *objects, *ob;
git_hashtable *table = NULL;
git_hashtable_iterator iterator;
table = git_hashtable_alloc(objects_n * 2, hash_func, hash_haskey);
must_be_true(table != NULL);
objects = git__malloc(objects_n * sizeof(table_item));
memset(objects, 0x0, objects_n * sizeof(table_item));
/* populate the hash table */
for (i = 0; i < objects_n; ++i) {
git_hash_buf(&(objects[i].id), &i, sizeof(int));
must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
}
git_hashtable_iterator_init(table, &iterator);
/* iterate through all nodes, mark as visited */
while ((ob = (table_item *)git_hashtable_iterator_next(&iterator)) != NULL)
ob->visited = 1;
/* make sure all nodes have been visited */
for (i = 0; i < objects_n; ++i)
must_be_true(objects[i].visited);
git_hashtable_free(table);
free(objects);
END_TEST
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