printf("ccan/hash\n");
printf("ccan/short_types\n");
printf("ccan/tal\n");
+ printf("ccan/typesafe_cb\n");
return 0;
}
ib->n_elems = n_elems;
ib->id_size = id_size;
ib->salt = salt;
+ ib->singleton = NULL;
ib->count = tal_arrz(ib, s32, n_elems);
ib->idsum = tal_arrz(ib, u8, id_size * n_elems);
if (!ib->count || !ib->idsum)
return ib;
}
+void invbloom_singleton_cb_(struct invbloom *ib,
+ void (*cb)(struct invbloom *,
+ size_t bucket, void *),
+ void *data)
+{
+ ib->singleton = cb;
+ ib->singleton_data = data;
+}
+
static size_t hash_bucket(const struct invbloom *ib, const void *id, size_t i)
{
return hash((const char *)id, ib->id_size, ib->salt+i*7) % ib->n_elems;
return (u8 *)ib->idsum + bucket * ib->id_size;
}
+static void check_for_singleton(struct invbloom *ib, size_t bucket)
+{
+ if (!ib->singleton)
+ return;
+
+ if (ib->count[bucket] != 1 && ib->count[bucket] != -1)
+ return;
+
+ ib->singleton(ib, bucket, ib->singleton_data);
+}
+
static void add_to_bucket(struct invbloom *ib, size_t n, const u8 *id)
{
size_t i;
for (i = 0; i < ib->id_size; i++)
idsum[i] ^= id[i];
+
+ check_for_singleton(ib, n);
}
static void remove_from_bucket(struct invbloom *ib, size_t n, const u8 *id)
ib->count[n]--;
for (i = 0; i < ib->id_size; i++)
idsum[i] ^= id[i];
+
+ check_for_singleton(ib, n);
}
void invbloom_insert(struct invbloom *ib, const void *id)
assert(ib1->id_size == ib2->id_size);
assert(ib1->salt == ib2->salt);
- for (i = 0; i < ib1->n_elems; i++)
- ib1->count[i] -= ib2->count[i];
-
for (i = 0; i < ib1->n_elems * ib1->id_size; i++)
ib1->idsum[i] ^= ib2->idsum[i];
+
+ for (i = 0; i < ib1->n_elems; i++) {
+ ib1->count[i] -= ib2->count[i];
+ check_for_singleton(ib1, i);
+ }
}
bool invbloom_empty(const struct invbloom *ib)
#ifndef CCAN_INVBLOOM_H
#define CCAN_INVBLOOM_H
#include <ccan/short_types/short_types.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
#include <ccan/tal/tal.h>
struct invbloom {
u32 salt;
s32 *count; /* [n_elems] */
u8 *idsum; /* [n_elems][id_size] */
+ void (*singleton)(struct invbloom *ib, size_t elem, void *);
+ void *singleton_data;
};
/**
size_t id_size,
size_t n_elems, u32 salt);
+/**
+ * invbloom_singleton_cb - set callback for when a singleton is found.
+ * @ib: the invertable bloom lookup table.
+ * @cb: the function to call (or NULL for none)
+ * @data: the data to hand to the function.
+ *
+ * This may be called by any function which mutates the table,
+ * possibly multiple times for a single call. The particular
+ * @ib bucket will be consistent, but the rest of the table may
+ * not be.
+ */
+#define invbloom_singleton_cb(ib, cb, data) \
+ invbloom_singleton_cb_((ib), \
+ typesafe_cb_preargs(void, void *, (cb), (data), \
+ struct invbloom *, size_t), (data))
+
+void invbloom_singleton_cb_(struct invbloom *ib,
+ void (*cb)(struct invbloom *,
+ size_t bucket, void *),
+ void *data);
/**
* invbloom_insert - add a new element
--- /dev/null
+#include <ccan/invbloom/invbloom.h>
+/* Include the C files directly. */
+#include <ccan/invbloom/invbloom.c>
+#include <ccan/tap/tap.h>
+
+static void singleton_cb(struct invbloom *ib, size_t n,
+ unsigned *count)
+{
+ ok1(ib->count[n] == 1 || ib->count[n] == -1);
+ (*count)++;
+}
+
+int main(void)
+{
+ struct invbloom *ib;
+ const tal_t *ctx = tal(NULL, char);
+ int val;
+ unsigned singleton_count;
+
+ /* This is how many tests you plan to run */
+ plan_tests(10 + 3 + NUM_HASHES * 2);
+
+ /* Single entry ib table keeps it simple. */
+ ib = invbloom_new(ctx, int, 1, 100);
+ invbloom_singleton_cb(ib, singleton_cb, &singleton_count);
+
+ val = 0;
+ singleton_count = 0;
+ invbloom_insert(ib, &val);
+ ok1(ib->count[0] == NUM_HASHES);
+ ok1(singleton_count == 1);
+ ok1(!invbloom_empty(ib));
+
+ /* First delete takes it via singleton. */
+ invbloom_delete(ib, &val);
+ ok1(singleton_count == 2);
+ ok1(invbloom_empty(ib));
+
+ /* Second delete creates negative singleton. */
+ invbloom_delete(ib, &val);
+ ok1(singleton_count == 3);
+
+ /* Now a larger table: this seed set so entries don't clash */
+ ib = invbloom_new(ctx, int, 1024, 0);
+ singleton_count = 0;
+ invbloom_singleton_cb(ib, singleton_cb, &singleton_count);
+
+ val = 0;
+ invbloom_insert(ib, &val);
+ ok1(singleton_count == NUM_HASHES);
+
+ /* First delete does nothing. */
+ invbloom_delete(ib, &val);
+ ok1(singleton_count == NUM_HASHES);
+ ok1(invbloom_empty(ib));
+
+ /* Second delete creates negative singletons. */
+ invbloom_delete(ib, &val);
+ ok1(singleton_count == NUM_HASHES * 2);
+
+ tal_free(ctx);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}