mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-18 21:35:20 +00:00
Refactor all 'vector' functions into common code
All the operations on the 'git_index_entry' array and the 'git_tree_entry' array have been refactored into common code in the src/vector.c file. The new vector methods support: - insertion: O(1) (avg) - deletion: O(n) - searching: O(logn) - sorting: O(logn) - r. access: O(1) Signed-off-by: Vicent Marti <tanoku@gmail.com>
This commit is contained in:
parent
1e35f929ef
commit
c4034e63f3
201
src/index.c
201
src/index.c
@ -99,6 +99,23 @@ static int read_tree(git_index *index, const char *buffer, size_t buffer_size);
|
|||||||
static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *);
|
static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *);
|
||||||
|
|
||||||
|
|
||||||
|
int index_srch(const void *key, const void *array_member)
|
||||||
|
{
|
||||||
|
const char *filename = (const char *)key;
|
||||||
|
const git_index_entry *entry = *(const git_index_entry **)(array_member);
|
||||||
|
|
||||||
|
return strcmp(filename, entry->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_index_entry *entry_a = *(const git_index_entry **)(a);
|
||||||
|
const git_index_entry *entry_b = *(const git_index_entry **)(b);
|
||||||
|
|
||||||
|
return strcmp(entry_a->path, entry_b->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path)
|
static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path)
|
||||||
{
|
{
|
||||||
git_index *index;
|
git_index *index;
|
||||||
@ -119,6 +136,8 @@ static int index_initialize(git_index **index_out, git_repository *owner, const
|
|||||||
|
|
||||||
index->repository = owner;
|
index->repository = owner;
|
||||||
|
|
||||||
|
git_vector_init(&index->entries, 32, index_cmp, index_srch);
|
||||||
|
|
||||||
/* Check if index file is stored on disk already */
|
/* Check if index file is stored on disk already */
|
||||||
if (gitfo_exists(index->index_file_path) == 0)
|
if (gitfo_exists(index->index_file_path) == 0)
|
||||||
index->on_disk = 1;
|
index->on_disk = 1;
|
||||||
@ -146,10 +165,14 @@ void git_index_clear(git_index *index)
|
|||||||
|
|
||||||
assert(index);
|
assert(index);
|
||||||
|
|
||||||
for (i = 0; i < index->entry_count; ++i)
|
for (i = 0; i < index->entries.length; ++i) {
|
||||||
free(index->entries[i].path);
|
git_index_entry *e;
|
||||||
|
e = git_vector_get(&index->entries, i);
|
||||||
|
free(e->path);
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
index->entry_count = 0;
|
git_vector_clear(&index->entries);
|
||||||
index->last_modified = 0;
|
index->last_modified = 0;
|
||||||
index->sorted = 1;
|
index->sorted = 1;
|
||||||
|
|
||||||
@ -163,8 +186,8 @@ void git_index_free(git_index *index)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
git_index_clear(index);
|
git_index_clear(index);
|
||||||
free(index->entries);
|
git_vector_free(&index->entries);
|
||||||
index->entries = NULL;
|
|
||||||
free(index->index_file_path);
|
free(index->index_file_path);
|
||||||
free(index);
|
free(index);
|
||||||
}
|
}
|
||||||
@ -241,13 +264,14 @@ int git_index_write(git_index *index)
|
|||||||
unsigned int git_index_entrycount(git_index *index)
|
unsigned int git_index_entrycount(git_index *index)
|
||||||
{
|
{
|
||||||
assert(index);
|
assert(index);
|
||||||
return index->entry_count;
|
return index->entries.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
git_index_entry *git_index_get(git_index *index, int n)
|
git_index_entry *git_index_get(git_index *index, int n)
|
||||||
{
|
{
|
||||||
assert(index);
|
assert(index);
|
||||||
return (n >= 0 && (unsigned int)n < index->entry_count) ? &index->entries[n] : NULL;
|
git_index__sort(index);
|
||||||
|
return git_vector_get(&index->entries, (unsigned int)n);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_index_add(git_index *index, const char *rel_path, int stage)
|
int git_index_add(git_index *index, const char *rel_path, int stage)
|
||||||
@ -297,31 +321,15 @@ int git_index_add(git_index *index, const char *rel_path, int stage)
|
|||||||
|
|
||||||
void git_index__sort(git_index *index)
|
void git_index__sort(git_index *index)
|
||||||
{
|
{
|
||||||
git_index_entry pivot;
|
if (index->sorted == 0) {
|
||||||
int i, j;
|
git_vector_sort(&index->entries);
|
||||||
|
|
||||||
if (index->sorted)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 1; i < (int)index->entry_count; ++i) {
|
|
||||||
|
|
||||||
memcpy(&pivot, &index->entries[i], sizeof(git_index_entry));
|
|
||||||
j = i - 1;
|
|
||||||
|
|
||||||
while (j >= 0 && strcmp(pivot.path, index->entries[j].path) < 0) {
|
|
||||||
memcpy(&index->entries[j + 1], &index->entries[j], sizeof(git_index_entry));
|
|
||||||
j = j - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&index->entries[j + 1], &pivot, sizeof(git_index_entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
index->sorted = 1;
|
index->sorted = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_index_insert(git_index *index, const git_index_entry *source_entry)
|
int git_index_insert(git_index *index, const git_index_entry *source_entry)
|
||||||
{
|
{
|
||||||
git_index_entry *offset;
|
git_index_entry *entry;
|
||||||
size_t path_length;
|
size_t path_length;
|
||||||
int position;
|
int position;
|
||||||
|
|
||||||
@ -330,107 +338,63 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry)
|
|||||||
if (source_entry->path == NULL)
|
if (source_entry->path == NULL)
|
||||||
return GIT_EMISSINGOBJDATA;
|
return GIT_EMISSINGOBJDATA;
|
||||||
|
|
||||||
position = git_index_find(index, source_entry->path);
|
entry = git__malloc(sizeof(git_index_entry));
|
||||||
|
if (entry == NULL)
|
||||||
if (position == GIT_ENOTFOUND) {
|
|
||||||
|
|
||||||
/* Resize the entries array */
|
|
||||||
if (index->entry_count + 1 > index->entries_size) {
|
|
||||||
git_index_entry *new_entries;
|
|
||||||
size_t new_size;
|
|
||||||
|
|
||||||
new_size = (unsigned int)(index->entries_size * 1.5f);
|
|
||||||
if (new_size < 8)
|
|
||||||
new_size = 8;
|
|
||||||
|
|
||||||
if ((new_entries = git__malloc(new_size * sizeof(git_index_entry))) == NULL)
|
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
memcpy(new_entries, index->entries, index->entry_count * sizeof(git_index_entry));
|
memcpy(entry, source_entry, sizeof(git_index_entry));
|
||||||
free(index->entries);
|
|
||||||
|
|
||||||
index->entries_size = new_size;
|
|
||||||
index->entries = new_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = &index->entries[index->entry_count];
|
|
||||||
index->entry_count++;
|
|
||||||
index->sorted = 0;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
offset = &index->entries[position];
|
|
||||||
free(offset->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(offset, source_entry, sizeof(git_index_entry));
|
|
||||||
|
|
||||||
/* duplicate the path string so we own it */
|
/* duplicate the path string so we own it */
|
||||||
offset->path = git__strdup(offset->path);
|
entry->path = git__strdup(entry->path);
|
||||||
if (offset->path == NULL)
|
if (entry->path == NULL)
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
/* make sure that the path length flag is correct */
|
/* make sure that the path length flag is correct */
|
||||||
path_length = strlen(offset->path);
|
path_length = strlen(entry->path);
|
||||||
|
|
||||||
offset->flags &= ~GIT_IDXENTRY_NAMEMASK;
|
entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
|
||||||
|
|
||||||
if (path_length < GIT_IDXENTRY_NAMEMASK)
|
if (path_length < GIT_IDXENTRY_NAMEMASK)
|
||||||
offset->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
|
entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
|
||||||
else
|
else
|
||||||
offset->flags |= GIT_IDXENTRY_NAMEMASK;;
|
entry->flags |= GIT_IDXENTRY_NAMEMASK;;
|
||||||
|
|
||||||
/* TODO: force the extended index entry flag? */
|
|
||||||
|
|
||||||
assert(offset->path);
|
/* look if an entry with this path already exists */
|
||||||
|
position = git_index_find(index, source_entry->path);
|
||||||
|
|
||||||
|
/* if no entry exists, add the entry at the end;
|
||||||
|
* the index is no longer sorted */
|
||||||
|
if (position == GIT_ENOTFOUND) {
|
||||||
|
if (git_vector_insert(&index->entries, entry) < 0)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
index->sorted = 0;
|
||||||
|
|
||||||
|
/* if a previous entry exists, replace it */
|
||||||
|
} else {
|
||||||
|
git_index_entry **entry_array = (git_index_entry **)index->entries.contents;
|
||||||
|
|
||||||
|
free(entry_array[position]->path);
|
||||||
|
free(entry_array[position]);
|
||||||
|
|
||||||
|
entry_array[position] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_index_remove(git_index *index, int position)
|
int git_index_remove(git_index *index, int position)
|
||||||
{
|
{
|
||||||
git_index_entry *offset;
|
|
||||||
size_t copy_size;
|
|
||||||
|
|
||||||
assert(index);
|
assert(index);
|
||||||
|
git_index__sort(index);
|
||||||
if (position < 0 || (unsigned int)position > index->entry_count)
|
return git_vector_remove(&index->entries, (unsigned int)position);
|
||||||
return GIT_ENOTFOUND;
|
|
||||||
|
|
||||||
offset = &index->entries[position];
|
|
||||||
index->entry_count--;
|
|
||||||
copy_size = (index->entry_count - position) * sizeof(git_index_entry);
|
|
||||||
|
|
||||||
memcpy(offset, offset + sizeof(git_index_entry), copy_size);
|
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_index_find(git_index *index, const char *path)
|
int git_index_find(git_index *index, const char *path)
|
||||||
{
|
{
|
||||||
int low = 0, high = index->entry_count;
|
|
||||||
|
|
||||||
if (!index->sorted)
|
|
||||||
git_index__sort(index);
|
git_index__sort(index);
|
||||||
|
return git_vector_search(&index->entries, path);
|
||||||
while (low < high) {
|
|
||||||
int mid = (low + high) >> 1;
|
|
||||||
int cmp = strcmp(path, index->entries[mid].path);
|
|
||||||
|
|
||||||
if (cmp < 0)
|
|
||||||
high = mid;
|
|
||||||
|
|
||||||
else if (cmp == 0) {
|
|
||||||
|
|
||||||
while (mid > 0 && strcmp(path, index->entries[mid - 1].path) == 0)
|
|
||||||
mid--;
|
|
||||||
|
|
||||||
return mid;
|
|
||||||
|
|
||||||
} else
|
|
||||||
low = mid + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GIT_ENOTFOUND; /* NOT FOUND */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void git_index_tree__free(git_index_tree *tree)
|
void git_index_tree__free(git_index_tree *tree)
|
||||||
@ -659,29 +623,30 @@ int git_index__parse(git_index *index, const char *buffer, size_t buffer_size)
|
|||||||
|
|
||||||
seek_forward(INDEX_HEADER_SIZE);
|
seek_forward(INDEX_HEADER_SIZE);
|
||||||
|
|
||||||
index->entry_count = header.entry_count;
|
git_vector_clear(&index->entries);
|
||||||
|
|
||||||
/* If there is already a entires array, reuse it if it can hold all the
|
|
||||||
* entries. If not, free and reallocate */
|
|
||||||
if (index->entry_count > index->entries_size) {
|
|
||||||
free(index->entries);
|
|
||||||
index->entries_size = (uint32_t)(index->entry_count * 1.3f);
|
|
||||||
index->entries = git__malloc(index->entries_size * sizeof(git_index_entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse all the entries */
|
/* Parse all the entries */
|
||||||
for (i = 0; i < index->entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
|
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
|
||||||
size_t entry_size;
|
size_t entry_size;
|
||||||
entry_size = read_entry(&index->entries[i], buffer, buffer_size);
|
git_index_entry *entry;
|
||||||
|
|
||||||
|
entry = git__malloc(sizeof(git_index_entry));
|
||||||
|
if (entry == NULL)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
entry_size = read_entry(entry, buffer, buffer_size);
|
||||||
|
|
||||||
/* 0 bytes read means an object corruption */
|
/* 0 bytes read means an object corruption */
|
||||||
if (entry_size == 0)
|
if (entry_size == 0)
|
||||||
return GIT_EOBJCORRUPTED;
|
return GIT_EOBJCORRUPTED;
|
||||||
|
|
||||||
|
if (git_vector_insert(&index->entries, entry) < 0)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
seek_forward(entry_size);
|
seek_forward(entry_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != index->entry_count)
|
if (i != header.entry_count)
|
||||||
return GIT_EOBJCORRUPTED;
|
return GIT_EOBJCORRUPTED;
|
||||||
|
|
||||||
/* There's still space for some extensions! */
|
/* There's still space for some extensions! */
|
||||||
@ -746,13 +711,13 @@ int git_index__write(git_index *index, git_filelock *file)
|
|||||||
WRITE_BYTES(INDEX_HEADER_SIG, 4);
|
WRITE_BYTES(INDEX_HEADER_SIG, 4);
|
||||||
|
|
||||||
WRITE_WORD(INDEX_VERSION_NUMBER);
|
WRITE_WORD(INDEX_VERSION_NUMBER);
|
||||||
WRITE_WORD(index->entry_count);
|
WRITE_WORD(index->entries.length);
|
||||||
|
|
||||||
for (i = 0; i < index->entry_count; ++i) {
|
for (i = 0; i < index->entries.length; ++i) {
|
||||||
git_index_entry *entry;
|
git_index_entry *entry;
|
||||||
size_t path_length, padding;
|
size_t path_length, padding;
|
||||||
|
|
||||||
entry = &index->entries[i];
|
entry = git_vector_get(&index->entries, i);
|
||||||
path_length = strlen(entry->path);
|
path_length = strlen(entry->path);
|
||||||
|
|
||||||
WRITE_WORD(entry->ctime.seconds);
|
WRITE_WORD(entry->ctime.seconds);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "fileops.h"
|
#include "fileops.h"
|
||||||
#include "filelock.h"
|
#include "filelock.h"
|
||||||
|
#include "vector.h"
|
||||||
#include "git/odb.h"
|
#include "git/odb.h"
|
||||||
#include "git/index.h"
|
#include "git/index.h"
|
||||||
|
|
||||||
@ -24,11 +25,8 @@ struct git_index {
|
|||||||
char *index_file_path;
|
char *index_file_path;
|
||||||
|
|
||||||
time_t last_modified;
|
time_t last_modified;
|
||||||
|
git_vector entries;
|
||||||
|
|
||||||
git_index_entry *entries;
|
|
||||||
unsigned int entries_size;
|
|
||||||
|
|
||||||
unsigned int entry_count;
|
|
||||||
unsigned int sorted:1,
|
unsigned int sorted:1,
|
||||||
on_disk:1;
|
on_disk:1;
|
||||||
|
|
||||||
|
111
src/tree.c
111
src/tree.c
@ -29,27 +29,7 @@
|
|||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "git/repository.h"
|
#include "git/repository.h"
|
||||||
|
|
||||||
static int resize_tree_array(git_tree *tree)
|
int entry_search_cmp(const void *key, const void *array_member)
|
||||||
{
|
|
||||||
git_tree_entry **new_entries;
|
|
||||||
|
|
||||||
tree->array_size *= 2;
|
|
||||||
if (tree->array_size == 0)
|
|
||||||
tree->array_size = 8;
|
|
||||||
|
|
||||||
new_entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
|
|
||||||
if (new_entries == NULL)
|
|
||||||
return GIT_ENOMEM;
|
|
||||||
|
|
||||||
memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry *));
|
|
||||||
|
|
||||||
free(tree->entries);
|
|
||||||
tree->entries = new_entries;
|
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int entry_cmp(const void *key, const void *array_member)
|
|
||||||
{
|
{
|
||||||
const char *filename = (const char *)key;
|
const char *filename = (const char *)key;
|
||||||
const git_tree_entry *entry = *(const git_tree_entry **)(array_member);
|
const git_tree_entry *entry = *(const git_tree_entry **)(array_member);
|
||||||
@ -65,24 +45,22 @@ int entry_sort_cmp(const void *a, const void *b)
|
|||||||
return strcmp(entry_a->filename, entry_b->filename);
|
return strcmp(entry_a->filename, entry_b->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void entry_resort(git_tree *tree)
|
|
||||||
{
|
|
||||||
qsort(tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_sort_cmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_tree_entries(git_tree *tree)
|
static void free_tree_entries(git_tree *tree)
|
||||||
{
|
{
|
||||||
size_t i;
|
unsigned int i;
|
||||||
|
|
||||||
if (tree == NULL)
|
if (tree == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < tree->entry_count; ++i) {
|
for (i = 0; i < tree->entries.length; ++i) {
|
||||||
free(tree->entries[i]->filename);
|
git_tree_entry *e;
|
||||||
free(tree->entries[i]);
|
e = git_vector_get(&tree->entries, i);
|
||||||
|
|
||||||
|
free(e->filename);
|
||||||
|
free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(tree->entries);
|
git_vector_free(&tree->entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -112,7 +90,7 @@ void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
|
|||||||
|
|
||||||
free(entry->filename);
|
free(entry->filename);
|
||||||
entry->filename = git__strdup(name);
|
entry->filename = git__strdup(name);
|
||||||
entry_resort(entry->owner);
|
git_vector_sort(&entry->owner->entries);
|
||||||
entry->owner->object.modified = 1;
|
entry->owner->object.modified = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,28 +127,27 @@ int git_tree_entry_2object(git_object **object_out, git_tree_entry *entry)
|
|||||||
|
|
||||||
git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
|
git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
|
||||||
{
|
{
|
||||||
git_tree_entry **found;
|
int idx;
|
||||||
|
|
||||||
assert(tree && filename);
|
assert(tree && filename);
|
||||||
|
|
||||||
found = bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
|
idx = git_vector_search(&tree->entries, filename);
|
||||||
return found ? *found : NULL;
|
if (idx == GIT_ENOTFOUND)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return git_vector_get(&tree->entries, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
|
git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
|
||||||
{
|
{
|
||||||
assert(tree);
|
assert(tree);
|
||||||
|
return git_vector_get(&tree->entries, (unsigned int)idx);
|
||||||
if (tree->entries == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (idx >= 0 && idx < (int)tree->entry_count) ? tree->entries[idx] : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t git_tree_entrycount(git_tree *tree)
|
size_t git_tree_entrycount(git_tree *tree)
|
||||||
{
|
{
|
||||||
assert(tree);
|
assert(tree);
|
||||||
return tree->entry_count;
|
return tree->entries.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
|
int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
|
||||||
@ -179,10 +156,6 @@ int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename,
|
|||||||
|
|
||||||
assert(tree && id && filename);
|
assert(tree && id && filename);
|
||||||
|
|
||||||
if (tree->entry_count >= tree->array_size)
|
|
||||||
if (resize_tree_array(tree) < 0)
|
|
||||||
return GIT_ENOMEM;
|
|
||||||
|
|
||||||
if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
|
if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
@ -193,8 +166,10 @@ int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename,
|
|||||||
entry->attr = attributes;
|
entry->attr = attributes;
|
||||||
entry->owner = tree;
|
entry->owner = tree;
|
||||||
|
|
||||||
tree->entries[tree->entry_count++] = entry;
|
if (git_vector_insert(&tree->entries, entry) < 0)
|
||||||
entry_resort(tree);
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
git_vector_sort(&tree->entries);
|
||||||
|
|
||||||
tree->object.modified = 1;
|
tree->object.modified = 1;
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
@ -206,32 +181,28 @@ int git_tree_remove_entry_byindex(git_tree *tree, int idx)
|
|||||||
|
|
||||||
assert(tree);
|
assert(tree);
|
||||||
|
|
||||||
if (idx < 0 || idx >= (int)tree->entry_count)
|
remove_ptr = git_vector_get(&tree->entries, (unsigned int)idx);
|
||||||
|
if (remove_ptr == NULL)
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
remove_ptr = tree->entries[idx];
|
|
||||||
tree->entries[idx] = tree->entries[--tree->entry_count];
|
|
||||||
|
|
||||||
free(remove_ptr->filename);
|
free(remove_ptr->filename);
|
||||||
free(remove_ptr);
|
free(remove_ptr);
|
||||||
entry_resort(tree);
|
|
||||||
|
|
||||||
tree->object.modified = 1;
|
tree->object.modified = 1;
|
||||||
return GIT_SUCCESS;
|
|
||||||
|
return git_vector_remove(&tree->entries, (unsigned int)idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
|
int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
|
||||||
{
|
{
|
||||||
git_tree_entry **entry_ptr;
|
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
assert(tree && filename);
|
assert(tree && filename);
|
||||||
|
|
||||||
entry_ptr = bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
|
idx = git_vector_search(&tree->entries, filename);
|
||||||
if (entry_ptr == NULL)
|
if (idx == GIT_ENOTFOUND)
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
idx = (int)(entry_ptr - tree->entries);
|
|
||||||
return git_tree_remove_entry_byindex(tree, idx);
|
return git_tree_remove_entry_byindex(tree, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,14 +213,15 @@ int git_tree__writeback(git_tree *tree, git_odb_source *src)
|
|||||||
|
|
||||||
assert(tree && src);
|
assert(tree && src);
|
||||||
|
|
||||||
if (tree->entries == NULL)
|
if (tree->entries.length == 0)
|
||||||
return GIT_EMISSINGOBJDATA;
|
return GIT_EMISSINGOBJDATA;
|
||||||
|
|
||||||
entry_resort(tree);
|
git_vector_sort(&tree->entries);
|
||||||
|
|
||||||
for (i = 0; i < tree->entry_count; ++i) {
|
for (i = 0; i < tree->entries.length; ++i) {
|
||||||
git_tree_entry *entry;
|
git_tree_entry *entry;
|
||||||
entry = tree->entries[i];
|
|
||||||
|
entry = git_vector_get(&tree->entries, i);
|
||||||
|
|
||||||
sprintf(filemode, "%06o ", entry->attr);
|
sprintf(filemode, "%06o ", entry->attr);
|
||||||
|
|
||||||
@ -265,31 +237,26 @@ int git_tree__writeback(git_tree *tree, git_odb_source *src)
|
|||||||
static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
|
static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
|
||||||
{
|
{
|
||||||
static const size_t avg_entry_size = 40;
|
static const size_t avg_entry_size = 40;
|
||||||
|
unsigned int expected_size;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
expected_size = (tree->object.source.raw.len / avg_entry_size) + 1;
|
||||||
|
|
||||||
free_tree_entries(tree);
|
free_tree_entries(tree);
|
||||||
|
if (git_vector_init(&tree->entries, expected_size, entry_sort_cmp, entry_search_cmp) < 0)
|
||||||
tree->entry_count = 0;
|
|
||||||
tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
|
|
||||||
tree->entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
|
|
||||||
|
|
||||||
if (tree->entries == NULL)
|
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
while (buffer < buffer_end) {
|
while (buffer < buffer_end) {
|
||||||
git_tree_entry *entry;
|
git_tree_entry *entry;
|
||||||
|
|
||||||
if (tree->entry_count >= tree->array_size)
|
|
||||||
if (resize_tree_array(tree) < 0)
|
|
||||||
return GIT_ENOMEM;
|
|
||||||
|
|
||||||
entry = git__malloc(sizeof(git_tree_entry));
|
entry = git__malloc(sizeof(git_tree_entry));
|
||||||
if (entry == NULL) {
|
if (entry == NULL) {
|
||||||
error = GIT_ENOMEM;
|
error = GIT_ENOMEM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree->entries[tree->entry_count++] = entry;
|
if (git_vector_insert(&tree->entries, entry) < 0)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
entry->owner = tree;
|
entry->owner = tree;
|
||||||
entry->attr = strtol(buffer, &buffer, 8);
|
entry->attr = strtol(buffer, &buffer, 8);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <git/tree.h>
|
#include <git/tree.h>
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
struct git_tree_entry {
|
struct git_tree_entry {
|
||||||
unsigned int attr;
|
unsigned int attr;
|
||||||
@ -14,10 +15,7 @@ struct git_tree_entry {
|
|||||||
|
|
||||||
struct git_tree {
|
struct git_tree {
|
||||||
git_object object;
|
git_object object;
|
||||||
|
git_vector entries;
|
||||||
git_tree_entry **entries;
|
|
||||||
size_t entry_count;
|
|
||||||
size_t array_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void git_tree__free(git_tree *tree);
|
void git_tree__free(git_tree *tree);
|
||||||
|
146
src/vector.c
Normal file
146
src/vector.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* 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 "vector.h"
|
||||||
|
|
||||||
|
static const double resize_factor = 1.75;
|
||||||
|
static const int minimum_size = 8;
|
||||||
|
|
||||||
|
static int resize_vector(git_vector *v)
|
||||||
|
{
|
||||||
|
void **new_contents;
|
||||||
|
|
||||||
|
v->_alloc_size *= resize_factor;
|
||||||
|
if (v->_alloc_size == 0)
|
||||||
|
v->_alloc_size = minimum_size;
|
||||||
|
|
||||||
|
new_contents = git__malloc(v->_alloc_size * sizeof(void *));
|
||||||
|
if (new_contents == NULL)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
memcpy(new_contents, v->contents, v->length * sizeof(void *));
|
||||||
|
|
||||||
|
free(v->contents);
|
||||||
|
v->contents = new_contents;
|
||||||
|
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void git_vector_free(git_vector *v)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
free(v->contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp, git_vector_srch srch)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
memset(v, 0x0, sizeof(git_vector));
|
||||||
|
|
||||||
|
if (initial_size == 0)
|
||||||
|
initial_size = minimum_size;
|
||||||
|
|
||||||
|
v->_alloc_size = initial_size;
|
||||||
|
v->_cmp = cmp;
|
||||||
|
v->_srch = srch;
|
||||||
|
|
||||||
|
v->length = 0;
|
||||||
|
|
||||||
|
v->contents = git__malloc(v->_alloc_size * sizeof(void *));
|
||||||
|
if (v->contents == NULL)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_vector_insert(git_vector *v, void *element)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
if (v->length >= v->_alloc_size) {
|
||||||
|
if (resize_vector(v) < 0)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
v->contents[v->length++] = element;
|
||||||
|
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *git_vector_get(git_vector *v, unsigned int position)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
return (position < v->length) ? v->contents[position] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_vector_sort(git_vector *v)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
if (v->_cmp != NULL)
|
||||||
|
qsort(v->contents, v->length, sizeof(void *), v->_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_vector_search(git_vector *v, const void *key)
|
||||||
|
{
|
||||||
|
void **find;
|
||||||
|
|
||||||
|
if (v->_srch == NULL)
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
|
find = bsearch(key, v->contents, v->length, sizeof(void *), v->_srch);
|
||||||
|
if (find == NULL)
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
|
return (int)(find - v->contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_vector_remove(git_vector *v, unsigned int idx)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
if (idx >= v->length || v->length == 0)
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
|
for (i = idx; i < v->length; ++i)
|
||||||
|
v->contents[i] = v->contents[i + 1];
|
||||||
|
|
||||||
|
v->length--;
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_vector_clear(git_vector *v)
|
||||||
|
{
|
||||||
|
assert(v);
|
||||||
|
v->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
32
src/vector.h
Normal file
32
src/vector.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef INCLUDE_vector_h__
|
||||||
|
#define INCLUDE_vector_h__
|
||||||
|
|
||||||
|
#include "git/common.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef int (*git_vector_cmp)(const void *, const void *);
|
||||||
|
typedef int (*git_vector_srch)(const void *, const void *);
|
||||||
|
|
||||||
|
typedef struct git_vector {
|
||||||
|
unsigned int _alloc_size;
|
||||||
|
git_vector_cmp _cmp;
|
||||||
|
git_vector_srch _srch;
|
||||||
|
|
||||||
|
void **contents;
|
||||||
|
unsigned int length;
|
||||||
|
} git_vector;
|
||||||
|
|
||||||
|
|
||||||
|
int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp, git_vector_srch srch);
|
||||||
|
void git_vector_free(git_vector *v);
|
||||||
|
void git_vector_clear(git_vector *v);
|
||||||
|
|
||||||
|
int git_vector_search(git_vector *v, const void *key);
|
||||||
|
void git_vector_sort(git_vector *v);
|
||||||
|
|
||||||
|
void *git_vector_get(git_vector *v, unsigned int position);
|
||||||
|
|
||||||
|
int git_vector_insert(git_vector *v, void *element);
|
||||||
|
int git_vector_remove(git_vector *v, unsigned int idx);
|
||||||
|
|
||||||
|
#endif
|
@ -35,7 +35,7 @@ BEGIN_TEST(index_loadempty_test)
|
|||||||
must_pass(git_index_read(index));
|
must_pass(git_index_read(index));
|
||||||
|
|
||||||
must_be_true(index->on_disk == 0);
|
must_be_true(index->on_disk == 0);
|
||||||
must_be_true(index->entry_count == 0);
|
must_be_true(git_index_entrycount(index) == 0);
|
||||||
must_be_true(index->sorted);
|
must_be_true(index->sorted);
|
||||||
|
|
||||||
git_index_free(index);
|
git_index_free(index);
|
||||||
@ -44,6 +44,7 @@ END_TEST
|
|||||||
BEGIN_TEST(index_load_test)
|
BEGIN_TEST(index_load_test)
|
||||||
git_index *index;
|
git_index *index;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
git_index_entry **entries;
|
||||||
|
|
||||||
must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
|
must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
|
||||||
must_be_true(index->on_disk);
|
must_be_true(index->on_disk);
|
||||||
@ -51,11 +52,13 @@ BEGIN_TEST(index_load_test)
|
|||||||
must_pass(git_index_read(index));
|
must_pass(git_index_read(index));
|
||||||
|
|
||||||
must_be_true(index->on_disk);
|
must_be_true(index->on_disk);
|
||||||
must_be_true(index->entry_count == TEST_INDEX_ENTRY_COUNT);
|
must_be_true(git_index_entrycount(index) == TEST_INDEX_ENTRY_COUNT);
|
||||||
must_be_true(index->sorted);
|
must_be_true(index->sorted);
|
||||||
|
|
||||||
|
entries = (git_index_entry **)index->entries.contents;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
|
for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
|
||||||
git_index_entry *e = &index->entries[TEST_ENTRIES[i].index];
|
git_index_entry *e = entries[TEST_ENTRIES[i].index];
|
||||||
|
|
||||||
must_be_true(strcmp(e->path, TEST_ENTRIES[i].path) == 0);
|
must_be_true(strcmp(e->path, TEST_ENTRIES[i].path) == 0);
|
||||||
must_be_true(e->mtime.seconds == TEST_ENTRIES[i].mtime);
|
must_be_true(e->mtime.seconds == TEST_ENTRIES[i].mtime);
|
||||||
@ -74,7 +77,7 @@ BEGIN_TEST(index2_load_test)
|
|||||||
must_pass(git_index_read(index));
|
must_pass(git_index_read(index));
|
||||||
|
|
||||||
must_be_true(index->on_disk);
|
must_be_true(index->on_disk);
|
||||||
must_be_true(index->entry_count == TEST_INDEX2_ENTRY_COUNT);
|
must_be_true(git_index_entrycount(index) == TEST_INDEX2_ENTRY_COUNT);
|
||||||
must_be_true(index->sorted);
|
must_be_true(index->sorted);
|
||||||
must_be_true(index->tree != NULL);
|
must_be_true(index->tree != NULL);
|
||||||
|
|
||||||
|
@ -7,26 +7,32 @@
|
|||||||
|
|
||||||
#define TEST_INDEX_PATH "../t0600-objects/index"
|
#define TEST_INDEX_PATH "../t0600-objects/index"
|
||||||
|
|
||||||
|
/*
|
||||||
void print_entries(git_index *index)
|
void print_entries(git_index *index)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < index->entry_count; ++i)
|
for (i = 0; i < index->entries.length; ++i)
|
||||||
printf("%d: %s\n", i, index->entries[i].path);
|
printf("%d: %s\n", i, index->entries[i].path);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void randomize_entries(git_index *index)
|
void randomize_entries(git_index *index)
|
||||||
{
|
{
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
git_index_entry tmp;
|
git_index_entry *tmp;
|
||||||
|
git_index_entry **entries;
|
||||||
|
|
||||||
|
entries = (git_index_entry **)index->entries.contents;
|
||||||
|
|
||||||
srand((unsigned int)time(NULL));
|
srand((unsigned int)time(NULL));
|
||||||
|
|
||||||
for (i = 0; i < index->entry_count; ++i) {
|
for (i = 0; i < index->entries.length; ++i) {
|
||||||
j = rand() % index->entry_count;
|
j = rand() % index->entries.length;
|
||||||
memcpy(&tmp, &index->entries[j], sizeof(git_index_entry));
|
|
||||||
memcpy(&index->entries[j], &index->entries[i], sizeof(git_index_entry));
|
tmp = entries[j];
|
||||||
memcpy(&index->entries[i], &tmp, sizeof(git_index_entry));
|
entries[j] = entries[i];
|
||||||
|
entries[i] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
index->sorted = 0;
|
index->sorted = 0;
|
||||||
@ -35,6 +41,9 @@ void randomize_entries(git_index *index)
|
|||||||
BEGIN_TEST(index_sort_test)
|
BEGIN_TEST(index_sort_test)
|
||||||
git_index *index;
|
git_index *index;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
git_index_entry **entries;
|
||||||
|
|
||||||
|
entries = (git_index_entry **)index->entries.contents;
|
||||||
|
|
||||||
must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
|
must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
|
||||||
must_pass(git_index_read(index));
|
must_pass(git_index_read(index));
|
||||||
@ -44,9 +53,8 @@ BEGIN_TEST(index_sort_test)
|
|||||||
git_index__sort(index);
|
git_index__sort(index);
|
||||||
must_be_true(index->sorted);
|
must_be_true(index->sorted);
|
||||||
|
|
||||||
for (i = 1; i < index->entry_count; ++i)
|
for (i = 1; i < index->entries.length; ++i)
|
||||||
must_be_true(strcmp(index->entries[i - 1].path,
|
must_be_true(strcmp(entries[i - 1]->path, entries[i]->path) < 0);
|
||||||
index->entries[i].path) < 0);
|
|
||||||
|
|
||||||
git_index_free(index);
|
git_index_free(index);
|
||||||
END_TEST
|
END_TEST
|
||||||
|
Loading…
Reference in New Issue
Block a user