#include "cache.h"
#include "cinnabar-notes.h"
#undef notes_tree
#undef init_notes
#undef free_notes
#undef add_note
#undef remove_note
#undef get_note
#undef for_each_note
#undef write_notes_tree
static int abbrev_sha1_cmp(const unsigned char *ref_sha1,
const unsigned char *abbrev_sha1, size_t len)
{
int i;
for (i = 0; i < len / 2; i++, ref_sha1++, abbrev_sha1++) {
if (*ref_sha1 != *abbrev_sha1)
return *ref_sha1 - *abbrev_sha1;
}
if (len % 2) {
unsigned char ref_bits = *ref_sha1 & 0xf0;
unsigned char abbrev_bits = *abbrev_sha1 & 0xf0;
if (ref_bits != abbrev_bits)
return ref_bits - abbrev_bits;
}
return 0;
}
struct int_node {
void *a[16];
};
struct leaf_node {
struct object_id key_oid;
struct object_id val_oid;
};
#define PTR_TYPE_NULL 0
#define PTR_TYPE_INTERNAL 1
#define PTR_TYPE_NOTE 2
#define PTR_TYPE_SUBTREE 3
#define GET_PTR_TYPE(ptr) ((uintptr_t) (ptr) & 3)
#define CLR_PTR_TYPE(ptr) ((void *) ((uintptr_t) (ptr) & ~3))
#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
static struct leaf_node *note_tree_abbrev_find(struct notes_tree *t,
struct int_node *tree, unsigned char n,
const unsigned char *key_sha1, size_t len)
{
unsigned char i, j;
void *p;
if (n >= len) {
for (i = 17, j = 0; j < 16; j++) {
if (tree->a[j])
i = (i < 17) ? 16 : j;
}
if (i >= 16)
return NULL;
} else {
i = GET_NIBBLE(n, key_sha1);
}
p = tree->a[i];
switch (GET_PTR_TYPE(p)) {
case PTR_TYPE_INTERNAL:
tree = CLR_PTR_TYPE(p);
return note_tree_abbrev_find(t, tree, ++n, key_sha1, len);
case PTR_TYPE_SUBTREE:
return NULL;
default:
{
struct leaf_node *node = CLR_PTR_TYPE(p);
if (node && !abbrev_sha1_cmp(node->key_oid.hash,
key_sha1, len))
return node;
return NULL;
}
}
}
const struct object_id *get_abbrev_note(struct cinnabar_notes_tree *t,
const struct object_id *object_oid, size_t len)
{
struct leaf_node *found;
assert(t);
assert(notes_initialized(t));
found = note_tree_abbrev_find(&t->current, t->current.root, 0,
object_oid->hash, len);
if (!found)
found = note_tree_abbrev_find(
&t->additions, t->additions.root, 0,
object_oid->hash, len);
return found ? &found->val_oid : NULL;
}
void cinnabar_init_notes(struct cinnabar_notes_tree *t, const char *notes_ref,
combine_notes_fn combine_notes, int flags)
{
t->init_flags = flags;
init_notes(&t->current, notes_ref, combine_notes, flags);
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
}
void cinnabar_free_notes(struct cinnabar_notes_tree *t)
{
free_notes(&t->current);
free_notes(&t->additions);
}
int cinnabar_add_note(
struct cinnabar_notes_tree *t, const struct object_id *object_oid,
const struct object_id *note_oid)
{
combine_notes_fn combine_notes = t->current.combine_notes;
if (combine_notes == combine_notes_ignore) {
if (get_note(&t->current, object_oid))
return 0;
} else if (combine_notes != combine_notes_overwrite) {
die("Unsupported combine_notes");
}
return add_note(&t->additions, object_oid, note_oid, NULL);
}
int cinnabar_remove_note(struct cinnabar_notes_tree *t,
const unsigned char *object_sha1)
{
int result = remove_note(&t->current, object_sha1);
int result2 = remove_note(&t->additions, object_sha1);
if (!result) {
struct object_id oid;
hashcpy(oid.hash, object_sha1);
oid.algo = GIT_HASH_SHA1;
add_note(&t->additions, &oid, null_oid(), NULL);
}
return result && result2;
}
const struct object_id *cinnabar_get_note(struct cinnabar_notes_tree *t,
const struct object_id *object_oid)
{
const struct object_id *note = get_note(&t->current, object_oid);
if (!note) {
note = get_note(&t->additions, object_oid);
if (note && is_null_oid(note))
note = NULL;
}
return note;
}
static int merge_note(const struct object_id *object_oid,
const struct object_id *note_oid, char *note_path,
void *data)
{
struct notes_tree *notes = (struct notes_tree *)data;
if (is_null_oid(note_oid))
remove_note(notes, object_oid->hash);
else
add_note(notes, object_oid, note_oid, combine_notes_overwrite);
return 0;
}
int cinnabar_for_each_note(struct cinnabar_notes_tree *t, int flags,
each_note_fn fn, void *cb_data)
{
consolidate_notes(t);
return for_each_note(&t->current, flags, fn, cb_data);
}
extern int write_notes_tree_mode(
struct notes_tree *t, struct object_id *result, unsigned int mode);
int cinnabar_write_notes_tree(struct cinnabar_notes_tree *t,
struct object_id *result, unsigned int mode)
{
consolidate_notes(t);
return write_notes_tree_mode(&t->current, result, mode);
}
void consolidate_notes(struct cinnabar_notes_tree *t) {
struct int_node empty_node = { { NULL, } };
char *notes_ref = xstrdup_or_null(t->current.ref);
if (memcmp(t->current.root, &empty_node, sizeof(empty_node)) == 0) {
free_notes(&t->current);
memcpy(&t->current, &t->additions, sizeof(struct notes_tree));
t->additions.initialized = 0;
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
} else if (memcmp(t->additions.root, &empty_node, sizeof(empty_node)) != 0) {
if (!t->current.dirty) {
combine_notes_fn combine_notes = t->current.combine_notes;
free_notes(&t->current);
init_notes(&t->current, notes_ref, combine_notes, t->init_flags);
}
for_each_note(&t->additions, FOR_EACH_NOTE_DONT_UNPACK_SUBTREES,
merge_note, &t->current);
free_notes(&t->additions);
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
}
free(notes_ref);
}