Skip to content

Commit

Permalink
ck_hs: Allow specifying an offset to embedded keys
Browse files Browse the repository at this point in the history
A common use case for hash sets is to store objects in
a hash sets keyed on some embedded key within the object.
The current interface requires the client to write hash
and comparator functions that are specific to each object
type being inserted into the hash.  Alternatively, the client
may store pointers to the keys in the hash set and use
`container_of` to recover the actual object value.  This is
also quite cumbersome.

This patch adds a function `ck_hs_init_from_options` that
initializes a hash set from an extensible struct of options.
The struct contains all the information currently passed to
`ck_hs_init`, and also allows specifying a short 15 bit
key offset when the hash set uses `CK_HS_MODE_OBJECT` mode.
The patch sets the following invariants:

- The public interface always specifies whether a parameter
pointer is to a key or a value.

- Hash and comparator functions are always called on
pointers to keys.

Fixes concurrencykit#152
  • Loading branch information
jpihlaja-bt committed Feb 11, 2022
1 parent 4adf131 commit 9757231
Show file tree
Hide file tree
Showing 5 changed files with 516 additions and 50 deletions.
58 changes: 58 additions & 0 deletions doc/ck_hs_init
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ Concurrency Kit (libck, \-lck)
.Fn ck_hs_compare_cb_t "const void *c1" "const void *c2"
.Ft bool
.Fn ck_hs_init "ck_hs_t *hs" "unsigned int mode" "ck_hs_hash_cb_t *hash_function" "ck_hs_compare_cb_t *compare" "struct ck_malloc *allocator" "unsigned long capacity" "unsigned long seed"
.Pp
.Dv struct ck_hs_init_options options = CK_HS_INIT_OPTIONS_INITIALIZER;
.Pp
.Dv #define CK_HS_INIT_OPTIONS_SIZE_V0 ...
.Pp
.Dv #define CK_HS_INIT_OPTIONS_SIZE_V1 ...
.Pp
.Bd -literal -offset
struct ck_hs_init_options {
/* v0 fields */
uintptr_t options_size;
uintptr_t mode;
ck_hs_hash_cb_t *hash_function;
ck_hs_compare_cb_t *compare;
struct ck_malloc *allocator;
uintptr_t capacity;
uintptr_t seed;
/* v1 fields */
uintptr_t key_offset;
};
.Ed
.Ft bool
.Fn ck_hs_init_from_options "ck_hs_t *hs" "const struct ck_hs_init_options *options"
.Sh DESCRIPTION
The
.Fn ck_hs_init
Expand Down Expand Up @@ -127,6 +150,41 @@ The argument
.Fa seed
specifies the initial seed used by the underlying hash function.
The user is free to choose a value of their choice.
.Pp
The
.Xr ck_hs_init_from_options 3
function takes all the values required for initializing the hash set
from a ck_hs_init_options struct. The options struct's first field
.Fa options_size
is used to version the options and must be initialized with either the
size of the struct, or one of the following:
.Bl -tag -width indent
.It CK_HS_INIT_OPTIONS_SIZE_V0
The options struct contains the fields
.Fa options_size ,
.Fa mode ,
.Fa hash_function ,
.Fa compare ,
.Fa allocator ,
.Fa capacity ,
and
.Fa seed .
The semantics for these fields is exactly as the corresponding arguments of
.Xr ck_hs_init 3 .
.It CK_HS_INIT_OPTIONS_SIZE_V1
The options struct additionally contains the field
.Fa key_offset .
If this field is non-zero, then the hash set must have mode
CK_HS_MODE_OBJECT set, and the field gives the offset of an embedded
key field within the object. The offset is subsequenty added to the
object to pass the address of the embedded key to the
.Fa hash_function
and
.Fa compare
functions.
The maximum supported offset is 65535.
.El
Future versions of concurrency kit may define more option fields.
.Sh RETURN VALUES
Upon successful completion
.Fn ck_hs_init
Expand Down
72 changes: 72 additions & 0 deletions include/ck_hs.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,77 @@ ck_hs_hash(const struct ck_hs *hs, const void *k)
return hs->hf(k, hs->seed);
}

/*
* An extensible struct of options for `ck_hs_init_from_options`. The
* fields in this struct must not be rearranged and ach field must
* have the same width as an uintptr_t. Adding new fields to this
* struct is forwards compatible.
*/
struct ck_hs_init_options {
/* -- V0 options start here -- */

/*
* The size of this options struct. This is set automatically
* by `CK_HS_INIT_OPTIONS_INITIALIZER`, or it should be set
* expicitly to the size of the smallest required version of
* the struct. Version specific sizes are given by
* `CK_HS_INIT_OPTIONS_SIZE_V<version>` constants.
*/
uintptr_t options_size;

/*
* Hash set mode.
*/
uintptr_t mode;

/*
* Key hash function.
*/
ck_hs_hash_cb_t *hash_function;

/*
* Key comparator function.
*/
ck_hs_compare_cb_t *compare;

/*
* Allocator used for the hash set.
*/
struct ck_malloc *allocator;

/*
* Initial capacity of the hash set.
*/
uintptr_t capacity;

/*
* Hash function seed.
*/
uintptr_t seed;

/* -- V1 options start here -- */

/*
* When mode is CK_HS_MODE_OBJECT, then the offset in bytes
* from the start of the object to the start of the key within
* the object. The hash and key comparator functions will
* then be called with the address of the embedded key rather
* than the object.
*/
uintptr_t key_offset;
};

/*
* The zeroth version of the options struct has only the same fields
* that `ck_hs_init` takes.
*/
#define CK_HS_INIT_OPTIONS_SIZE_V0 (7 * sizeof(uintptr_t))

/* The first version of the options struct adds `key_offset`. */
#define CK_HS_INIT_OPTIONS_SIZE_V1 (8 * sizeof(uintptr_t))

#define CK_HS_INIT_OPTIONS_INITIALIZER { .options_size = sizeof(struct ck_hs_init_options) }

typedef void *ck_hs_apply_fn_t(void *, void *);
bool ck_hs_apply(ck_hs_t *, unsigned long, const void *, ck_hs_apply_fn_t *, void *);
void ck_hs_iterator_init(ck_hs_iterator_t *);
Expand All @@ -126,6 +197,7 @@ bool ck_hs_move(ck_hs_t *, ck_hs_t *, ck_hs_hash_cb_t *,
ck_hs_compare_cb_t *, struct ck_malloc *);
bool ck_hs_init(ck_hs_t *, unsigned int, ck_hs_hash_cb_t *,
ck_hs_compare_cb_t *, struct ck_malloc *, unsigned long, unsigned long);
bool ck_hs_init_from_options(ck_hs_t *, const struct ck_hs_init_options *);
void ck_hs_destroy(ck_hs_t *);
void *ck_hs_get(ck_hs_t *, unsigned long, const void *);
bool ck_hs_put(ck_hs_t *, unsigned long, const void *);
Expand Down
5 changes: 4 additions & 1 deletion regressions/ck_hs/validate/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
.PHONY: check clean distribution

OBJECTS=serial
OBJECTS=serial hs_init_opts

all: $(OBJECTS)

serial: serial.c ../../../include/ck_hs.h ../../../src/ck_hs.c
$(CC) $(CFLAGS) -o serial serial.c ../../../src/ck_hs.c

hs_init_opts: hs_init_opts.c ../../../include/ck_hs.h ../../../src/ck_hs.c
$(CC) $(CFLAGS) -o hs_init_opts hs_init_opts.c ../../../src/ck_hs.c

check: all
./serial

Expand Down
Loading

0 comments on commit 9757231

Please sign in to comment.