Skip to content

Commit 9e6fabd

Browse files
jonathantanmygitster
authored andcommitted
oidmap: map with OID as key
This is similar to using the hashmap in hashmap.c, but with an easier-to-use API. In particular, custom entry comparisons no longer need to be written, and lookups can be done without constructing a temporary entry structure. This is implemented as a thin wrapper over the hashmap API. In particular, this means that there is an additional 4-byte overhead due to the fact that the first 4 bytes of the hash is redundantly stored. For now, I'm taking the simpler approach, but if need be, we can reimplement oidmap without affecting the callers significantly. oidset has been updated to use oidmap. Signed-off-by: Jonathan Tan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ea220ee commit 9e6fabd

File tree

6 files changed

+133
-31
lines changed

6 files changed

+133
-31
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ LIB_OBJS += notes-cache.o
821821
LIB_OBJS += notes-merge.o
822822
LIB_OBJS += notes-utils.o
823823
LIB_OBJS += object.o
824+
LIB_OBJS += oidmap.o
824825
LIB_OBJS += oidset.o
825826
LIB_OBJS += packfile.o
826827
LIB_OBJS += pack-bitmap.o

fetch-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ static int tip_oids_contain(struct oidset *tip_oids,
611611
* add to "newlist" between calls, the additions will always be for
612612
* oids that are already in the set.
613613
*/
614-
if (!tip_oids->map.tablesize) {
614+
if (!tip_oids->map.map.tablesize) {
615615
add_refs_to_oidset(tip_oids, unmatched);
616616
add_refs_to_oidset(tip_oids, newlist);
617617
}

oidmap.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "cache.h"
2+
#include "oidmap.h"
3+
4+
static int cmpfn(const void *hashmap_cmp_fn_data,
5+
const void *entry, const void *entry_or_key,
6+
const void *keydata)
7+
{
8+
const struct oidmap_entry *entry_ = entry;
9+
if (keydata)
10+
return oidcmp(&entry_->oid, (const struct object_id *) keydata);
11+
return oidcmp(&entry_->oid,
12+
&((const struct oidmap_entry *) entry_or_key)->oid);
13+
}
14+
15+
static int hash(const struct object_id *oid)
16+
{
17+
int hash;
18+
memcpy(&hash, oid->hash, sizeof(hash));
19+
return hash;
20+
}
21+
22+
void oidmap_init(struct oidmap *map, size_t initial_size)
23+
{
24+
hashmap_init(&map->map, cmpfn, NULL, initial_size);
25+
}
26+
27+
void oidmap_free(struct oidmap *map, int free_entries)
28+
{
29+
if (!map)
30+
return;
31+
hashmap_free(&map->map, free_entries);
32+
}
33+
34+
void *oidmap_get(const struct oidmap *map, const struct object_id *key)
35+
{
36+
return hashmap_get_from_hash(&map->map, hash(key), key);
37+
}
38+
39+
void *oidmap_remove(struct oidmap *map, const struct object_id *key)
40+
{
41+
struct hashmap_entry entry;
42+
hashmap_entry_init(&entry, hash(key));
43+
return hashmap_remove(&map->map, &entry, key);
44+
}
45+
46+
void *oidmap_put(struct oidmap *map, void *entry)
47+
{
48+
struct oidmap_entry *to_put = entry;
49+
hashmap_entry_init(&to_put->internal_entry, hash(&to_put->oid));
50+
return hashmap_put(&map->map, to_put);
51+
}

oidmap.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#ifndef OIDMAP_H
2+
#define OIDMAP_H
3+
4+
#include "hashmap.h"
5+
6+
/*
7+
* struct oidmap_entry is a structure representing an entry in the hash table,
8+
* which must be used as first member of user data structures.
9+
*
10+
* Users should set the oid field. oidmap_put() will populate the
11+
* internal_entry field.
12+
*/
13+
struct oidmap_entry {
14+
/* For internal use only */
15+
struct hashmap_entry internal_entry;
16+
17+
struct object_id oid;
18+
};
19+
20+
struct oidmap {
21+
struct hashmap map;
22+
};
23+
24+
#define OIDMAP_INIT { { NULL } }
25+
26+
/*
27+
* Initializes an oidmap structure.
28+
*
29+
* `map` is the oidmap to initialize.
30+
*
31+
* If the total number of entries is known in advance, the `initial_size`
32+
* parameter may be used to preallocate a sufficiently large table and thus
33+
* prevent expensive resizing. If 0, the table is dynamically resized.
34+
*/
35+
extern void oidmap_init(struct oidmap *map, size_t initial_size);
36+
37+
/*
38+
* Frees an oidmap structure and allocated memory.
39+
*
40+
* If `free_entries` is true, each oidmap_entry in the map is freed as well
41+
* using stdlibs free().
42+
*/
43+
extern void oidmap_free(struct oidmap *map, int free_entries);
44+
45+
/*
46+
* Returns the oidmap entry for the specified oid, or NULL if not found.
47+
*/
48+
extern void *oidmap_get(const struct oidmap *map,
49+
const struct object_id *key);
50+
51+
/*
52+
* Adds or replaces an oidmap entry.
53+
*
54+
* ((struct oidmap_entry *) entry)->internal_entry will be populated by this
55+
* function.
56+
*
57+
* Returns the replaced entry, or NULL if not found (i.e. the entry was added).
58+
*/
59+
extern void *oidmap_put(struct oidmap *map, void *entry);
60+
61+
/*
62+
* Removes an oidmap entry matching the specified oid.
63+
*
64+
* Returns the removed entry, or NULL if not found.
65+
*/
66+
extern void *oidmap_remove(struct oidmap *map, const struct object_id *key);
67+
68+
#endif

oidset.c

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,30 @@
11
#include "cache.h"
22
#include "oidset.h"
33

4-
struct oidset_entry {
5-
struct hashmap_entry hash;
6-
struct object_id oid;
7-
};
8-
9-
static int oidset_hashcmp(const void *unused_cmp_data,
10-
const void *va, const void *vb,
11-
const void *vkey)
12-
{
13-
const struct oidset_entry *a = va, *b = vb;
14-
const struct object_id *key = vkey;
15-
return oidcmp(&a->oid, key ? key : &b->oid);
16-
}
17-
184
int oidset_contains(const struct oidset *set, const struct object_id *oid)
195
{
20-
struct hashmap_entry key;
21-
22-
if (!set->map.cmpfn)
6+
if (!set->map.map.tablesize)
237
return 0;
24-
25-
hashmap_entry_init(&key, sha1hash(oid->hash));
26-
return !!hashmap_get(&set->map, &key, oid);
8+
return !!oidmap_get(&set->map, oid);
279
}
2810

2911
int oidset_insert(struct oidset *set, const struct object_id *oid)
3012
{
31-
struct oidset_entry *entry;
32-
33-
if (!set->map.cmpfn)
34-
hashmap_init(&set->map, oidset_hashcmp, NULL, 0);
13+
struct oidmap_entry *entry;
3514

36-
if (oidset_contains(set, oid))
15+
if (!set->map.map.tablesize)
16+
oidmap_init(&set->map, 0);
17+
else if (oidset_contains(set, oid))
3718
return 1;
3819

3920
entry = xmalloc(sizeof(*entry));
40-
hashmap_entry_init(&entry->hash, sha1hash(oid->hash));
4121
oidcpy(&entry->oid, oid);
4222

43-
hashmap_add(&set->map, entry);
23+
oidmap_put(&set->map, entry);
4424
return 0;
4525
}
4626

4727
void oidset_clear(struct oidset *set)
4828
{
49-
hashmap_free(&set->map, 1);
29+
oidmap_free(&set->map, 1);
5030
}

oidset.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef OIDSET_H
22
#define OIDSET_H
33

4+
#include "oidmap.h"
5+
46
/**
57
* This API is similar to sha1-array, in that it maintains a set of object ids
68
* in a memory-efficient way. The major differences are:
@@ -17,10 +19,10 @@
1719
* A single oidset; should be zero-initialized (or use OIDSET_INIT).
1820
*/
1921
struct oidset {
20-
struct hashmap map;
22+
struct oidmap map;
2123
};
2224

23-
#define OIDSET_INIT { { NULL } }
25+
#define OIDSET_INIT { OIDMAP_INIT }
2426

2527
/**
2628
* Returns true iff `set` contains `oid`.

0 commit comments

Comments
 (0)