From 19524a49d4389bff6f7ba9d9c355489450579c01 Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Mon, 30 Mar 2020 14:21:21 -0500 Subject: [PATCH] opr: Allow non-2^x for n_buckets in opr_cache_init Currently, opr_cache_init requires that opts->n_buckets is a power of 2 (since our underlying opr_dict requires this). However, callers may want to pick a number of buckets based on some other value. Requiring each caller to calculate the nearest power-of-2 is annoying, so instead just have opr_cache_init itself calculate a nearby power of 2. That is, with this commit, opts->n_buckets is allowed to not be a power of 2; when it's not a power of 2, opr_cache_init will calculate the next highest power of 2 and use that as the number of buckets. Change-Id: Icd3c56c1fe0733e3dac964ea9a98ff7b436254e6 Reviewed-on: https://gerrit.openafs.org/14122 Tested-by: BuildBot Reviewed-by: Marcio Brito Barbosa Reviewed-by: Benjamin Kaduk --- src/opr/cache.c | 28 +++++++++++++++++++++++----- tests/opr/cache-t.c | 24 +++++++++++++++++++++--- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/opr/cache.c b/src/opr/cache.c index 82b759dfa8..54fbf3ea2b 100644 --- a/src/opr/cache.c +++ b/src/opr/cache.c @@ -356,6 +356,21 @@ isPowerOf2(int value) return (value > 0) && (value & (value - 1)) == 0; } +static_inline int +nextPowerOf2(int target) +{ + int next = 1; + /* + * Make sure we have a reasonable target and cannot overflow; callers + * should do their own range checks before we get here. + */ + opr_Assert(target > 0 && target <= 0x40000000); + while (next < target) { + next *= 2; + } + return next; +} + /** * Create a new opr_cache. * @@ -369,17 +384,20 @@ int opr_cache_init(struct opr_cache_opts *opts, struct opr_cache **a_cache) { struct opr_cache *cache; + int n_buckets = opts->n_buckets; - if (opts->n_buckets < MIN_BUCKETS || opts->n_buckets > MAX_BUCKETS) { - return EINVAL; - } - if (!isPowerOf2(opts->n_buckets)) { + if (n_buckets < MIN_BUCKETS || n_buckets > MAX_BUCKETS) { return EINVAL; } if (opts->max_entries < MIN_ENTRIES || opts->max_entries > MAX_ENTRIES) { return EINVAL; } + n_buckets = nextPowerOf2(n_buckets); + opr_Assert(isPowerOf2(n_buckets)); + opr_Assert(n_buckets >= MIN_BUCKETS); + opr_Assert(n_buckets <= MAX_BUCKETS); + cache = calloc(1, sizeof(*cache)); if (cache == NULL) { return ENOMEM; @@ -388,7 +406,7 @@ opr_cache_init(struct opr_cache_opts *opts, struct opr_cache **a_cache) opr_mutex_init(&cache->lock); cache->max_entries = opts->max_entries; - cache->dict = opr_dict_Init(opts->n_buckets); + cache->dict = opr_dict_Init(n_buckets); if (cache->dict == NULL) { opr_cache_free(&cache); return ENOMEM; diff --git a/tests/opr/cache-t.c b/tests/opr/cache-t.c index 522bfaf7cf..2d97b2fc7a 100644 --- a/tests/opr/cache-t.c +++ b/tests/opr/cache-t.c @@ -102,9 +102,27 @@ run_seed(int seed) ok(opr_cache_init(&opts, &cache) != 0, "Initializing a cache with a huge n_buckets fails"); - opts.n_buckets = 23; + opts.n_buckets = 1024*1024 + 1; ok(opr_cache_init(&opts, &cache) != 0, - "Initializing a cache with non-power-of-2 n_buckets fails"); + "Initializing a cache with 1024*1024+1 n_buckets fails"); + + opts.n_buckets = 1024*1024; + code = opr_cache_init(&opts, &cache); + is_int(0, code, + "Initializing a cache with 1024*1024 n_buckets succeeds"); + opr_cache_free(&cache); + + opts.n_buckets = 1024*1024 - 1; + code = opr_cache_init(&opts, &cache); + is_int(0, code, + "Initializing a cache with 1024*1024-1 n_buckets succeeds"); + opr_cache_free(&cache); + + opts.n_buckets = 23; + code = opr_cache_init(&opts, &cache); + is_int(0, code, + "Initializing a cache with non-power-of-2 n_buckets succeeds"); + opr_cache_free(&cache); opts.n_buckets = 64; opts.max_entries = 1; @@ -237,7 +255,7 @@ main(void) { int seed; - plan(113 * 32); + plan(116 * 32); for (seed = 0; seed < 32; seed++) { run_seed(seed);