Coverage Report

Created: 2026-07-16 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/histo.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <assert.h>
15
#include <errno.h>
16
#include <math.h>
17
#include <stdbool.h>
18
#include <stdint.h>
19
#include <stdio.h>
20
#include <stdlib.h>
21
#include <string.h>
22
23
#include <isc/atomic.h>
24
#include <isc/bit.h>
25
#include <isc/histo.h>
26
#include <isc/magic.h>
27
#include <isc/mem.h>
28
#include <isc/tid.h>
29
30
0
#define HISTO_MAGIC     ISC_MAGIC('H', 's', 't', 'o')
31
#define HISTO_VALID(p)      ISC_MAGIC_VALID(p, HISTO_MAGIC)
32
0
#define HISTOMULTI_MAGIC    ISC_MAGIC('H', 'g', 'M', 't')
33
#define HISTOMULTI_VALID(p) ISC_MAGIC_VALID(p, HISTOMULTI_MAGIC)
34
35
/*
36
 * Natural logarithms of 2 and 10 for converting precisions between
37
 * binary and decimal significant figures
38
 */
39
0
#define LN_2  0.693147180559945309
40
0
#define LN_10 2.302585092994045684
41
42
/*
43
 * The chunks array has a static size for simplicity, fixed as the
44
 * number of bits in a value. That means we waste a little extra space
45
 * that could be saved by omitting the exponents that are covered by
46
 * `sigbits`. The following macros calculate (at run time) the exact
47
 * number of buckets when we need to do accurate bounds checks.
48
 *
49
 * For a discussion of the floating point terminology, see the
50
 * commmentary on `value_to_key()` below.
51
 *
52
 * We often use the variable names `c` for chunk and `b` for bucket.
53
 */
54
0
#define CHUNKS 64
55
56
0
#define DENORMALS(hg) ((hg)->sigbits - 1)
57
0
#define MANTISSAS(hg) (1 << (hg)->sigbits)
58
0
#define EXPONENTS(hg) (CHUNKS - DENORMALS(hg))
59
0
#define BUCKETS(hg)   (EXPONENTS(hg) * MANTISSAS(hg))
60
61
0
#define MAXCHUNK(hg)  EXPONENTS(hg)
62
0
#define CHUNKSIZE(hg) MANTISSAS(hg)
63
64
typedef atomic_uint_fast64_t hg_bucket_t;
65
typedef atomic_ptr(hg_bucket_t) hg_chunk_t;
66
67
struct isc_histo {
68
  unsigned int magic;
69
  unsigned int sigbits;
70
  isc_mem_t *mctx;
71
  hg_chunk_t chunk[CHUNKS];
72
};
73
74
struct isc_histomulti {
75
  unsigned int magic;
76
  unsigned int size;
77
  isc_refcount_t references;
78
  isc_histo_t *hg[];
79
};
80
81
/**********************************************************************/
82
83
void
84
0
isc_histo_create(isc_mem_t *mctx, unsigned int sigbits, isc_histo_t **hgp) {
85
0
  REQUIRE(sigbits >= ISC_HISTO_MINBITS);
86
0
  REQUIRE(sigbits <= ISC_HISTO_MAXBITS);
87
0
  REQUIRE(hgp != NULL);
88
0
  REQUIRE(*hgp == NULL);
89
90
0
  isc_histo_t *hg = isc_mem_get(mctx, sizeof(*hg));
91
0
  *hg = (isc_histo_t){
92
0
    .magic = HISTO_MAGIC,
93
0
    .sigbits = sigbits,
94
0
  };
95
0
  isc_mem_attach(mctx, &hg->mctx);
96
97
0
  *hgp = hg;
98
0
}
99
100
void
101
0
isc_histo_destroy(isc_histo_t **hgp) {
102
0
  REQUIRE(hgp != NULL);
103
0
  REQUIRE(HISTO_VALID(*hgp));
104
105
0
  isc_histo_t *hg = *hgp;
106
0
  *hgp = NULL;
107
108
0
  for (size_t c = 0; c < CHUNKS; c++) {
109
0
    if (hg->chunk[c] != NULL) {
110
0
      isc_mem_cput(hg->mctx, hg->chunk[c], CHUNKSIZE(hg),
111
0
             sizeof(hg_bucket_t));
112
0
    }
113
0
  }
114
0
  isc_mem_putanddetach(&hg->mctx, hg, sizeof(*hg));
115
0
}
116
117
/**********************************************************************/
118
119
unsigned int
120
0
isc_histo_sigbits(isc_histo_t *hg) {
121
0
  REQUIRE(HISTO_VALID(hg));
122
0
  return hg->sigbits;
123
0
}
124
125
/*
126
 * use precomputed logs and builtins to avoid linking with libm
127
 */
128
129
unsigned int
130
0
isc_histo_bits_to_digits(unsigned int bits) {
131
0
  REQUIRE(bits >= ISC_HISTO_MINBITS);
132
0
  REQUIRE(bits <= ISC_HISTO_MAXBITS);
133
0
  return floor(1.0 - (1.0 - bits) * LN_2 / LN_10);
134
0
}
135
136
unsigned int
137
0
isc_histo_digits_to_bits(unsigned int digits) {
138
0
  REQUIRE(digits >= ISC_HISTO_MINDIGITS);
139
0
  REQUIRE(digits <= ISC_HISTO_MAXDIGITS);
140
0
  return ceil(1.0 - (1.0 - digits) * LN_10 / LN_2);
141
0
}
142
143
/**********************************************************************/
144
145
/*
146
 * The way we map buckets to keys is what gives the histogram a
147
 * consistent relative error across the whole range of `uint64_t`.
148
 * The mapping is log-linear: a chunk key is the logarithm of part
149
 * of the value (in other words, chunks are spaced exponentially);
150
 * and a bucket within a chunk is a linear function of another part
151
 * of the value.
152
 *
153
 * This log-linear spacing is similar to the size classes used by
154
 * jemalloc. It is also the way floating point numbers work: the
155
 * exponent is the log part, and the mantissa is the linear part.
156
 *
157
 * So, a chunk number is the log (base 2) of a `uint64_t`, which is
158
 * between 0 and 63, which is why there are up to 64 chunks. In
159
 * floating point terms the chunk number is the exponent. The
160
 * histogram's number of significant bits is the size of the
161
 * mantissa, which indexes buckets within each chunk.
162
 *
163
 * A fast way to get the logarithm of a positive integer is CLZ,
164
 * count leading zeroes.
165
 *
166
 * Chunk zero is special. Chunk 1 covers values between `CHUNKSIZE`
167
 * and `CHUNKSIZE * 2 - 1`, where `CHUNKSIZE == exponent << sigbits
168
 * == 1 << sigbits`. Each chunk has CHUNKSIZE buckets, so chunk 1 has
169
 * one value per bucket. There are CHUNKSIZE values before chunk 1
170
 * which map to chunk 0, so it also has one value per bucket. (Hence
171
 * the first two chunks have one value per bucket.) The values in
172
 * chunk 0 correspond to denormal numbers in floating point terms.
173
 * They are also the values where `63 - sigbits - clz` would be less
174
 * than one if denormals were not handled specially.
175
 *
176
 * This branchless conversion is due to Paul Khuong: see bin_down_of() in
177
 * https://pvk.ca/Blog/2015/06/27/linear-log-bucketing-fast-versatile-simple/
178
 *
179
 * This function is in the `isc_histo_inc()` fast path.
180
 */
181
static inline unsigned int
182
0
value_to_key(const isc_histo_t *hg, uint64_t value) {
183
  /* ensure that denormal numbers are all in chunk zero */
184
0
  uint64_t chunked = value | CHUNKSIZE(hg);
185
0
  int clz = stdc_leading_zeros(chunked);
186
  /* actually 1 less than the exponent except for denormals */
187
0
  unsigned int exponent = 63 - hg->sigbits - clz;
188
  /* mantissa has leading bit set except for denormals */
189
0
  unsigned int mantissa = value >> exponent;
190
  /* leading bit of mantissa adds one to exponent */
191
0
  return (exponent << hg->sigbits) + mantissa;
192
0
}
193
194
/*
195
 * Inverse functions of `value_to_key()`, to get the minimum and
196
 * maximum values that map to a particular key.
197
 *
198
 * We must not cause undefined behaviour by hitting integer limits,
199
 * which is a risk when we aim to cover the entire range of `uint64_t`.
200
 *
201
 * The maximum value in the last bucket is UNSIGNED INT64_MAX, which
202
 * `key_to_maxval()` gets by deliberately subtracting `0 - 1`,
203
 * undeflowing a `uint64_t`. That is OK when unsigned.
204
 *
205
 * We must take care not to shift too much in `key_to_minval()`.
206
 * The largest key passed by `key_to_maxval()` is `BUCKETS(hg)`, so
207
 *  `exponent == EXPONENTS(hg) - 1 == 64 - sigbits`
208
 * which is always less than 64, so the size of the shift is OK.
209
 *
210
 * The `mantissa` in this edge case is just `chunksize`, which when
211
 * shifted becomes `1 << 64` which overflows `uint64_t` Again this is
212
 * OK when unsigned, so the return value is zero.
213
 */
214
215
static inline uint64_t
216
0
key_to_minval(const isc_histo_t *hg, unsigned int key) {
217
0
  unsigned int chunksize = CHUNKSIZE(hg);
218
0
  unsigned int exponent = (key / chunksize) - 1;
219
0
  uint64_t mantissa = (key % chunksize) + chunksize;
220
0
  return key < chunksize ? key : mantissa << exponent;
221
0
}
222
223
static inline uint64_t
224
0
key_to_maxval(const isc_histo_t *hg, unsigned int key) {
225
0
  return key_to_minval(hg, key + 1) - 1;
226
0
}
227
228
/**********************************************************************/
229
230
static hg_bucket_t *
231
0
key_to_new_bucket(isc_histo_t *hg, unsigned int key) {
232
  /* slow path */
233
0
  unsigned int chunksize = CHUNKSIZE(hg);
234
0
  unsigned int chunk = key / chunksize;
235
0
  unsigned int bucket = key % chunksize;
236
0
  hg_bucket_t *old_cp = NULL;
237
0
  hg_bucket_t *new_cp = isc_mem_cget(hg->mctx, CHUNKSIZE(hg),
238
0
             sizeof(hg_bucket_t));
239
0
  hg_chunk_t *cpp = &hg->chunk[chunk];
240
0
  if (atomic_compare_exchange_strong_acq_rel(cpp, &old_cp, new_cp)) {
241
0
    return &new_cp[bucket];
242
0
  } else {
243
    /* lost the race, so use the winner's chunk */
244
0
    isc_mem_cput(hg->mctx, new_cp, CHUNKSIZE(hg),
245
0
           sizeof(hg_bucket_t));
246
0
    return &old_cp[bucket];
247
0
  }
248
0
}
249
250
static hg_bucket_t *
251
0
get_chunk(const isc_histo_t *hg, unsigned int chunk) {
252
0
  return atomic_load_acquire(&hg->chunk[chunk]);
253
0
}
254
255
static inline hg_bucket_t *
256
0
key_to_bucket(const isc_histo_t *hg, unsigned int key) {
257
  /* fast path */
258
0
  unsigned int chunksize = CHUNKSIZE(hg);
259
0
  unsigned int chunk = key / chunksize;
260
0
  unsigned int bucket = key % chunksize;
261
0
  hg_bucket_t *cp = get_chunk(hg, chunk);
262
0
  return cp == NULL ? NULL : &cp[bucket];
263
0
}
264
265
static inline uint64_t
266
0
bucket_count(const hg_bucket_t *bp) {
267
0
  return bp == NULL ? 0 : atomic_load_relaxed(bp);
268
0
}
269
270
static inline uint64_t
271
0
get_key_count(const isc_histo_t *hg, unsigned int key) {
272
0
  return bucket_count(key_to_bucket(hg, key));
273
0
}
274
275
static inline void
276
0
add_key_count(isc_histo_t *hg, unsigned int key, uint64_t inc) {
277
  /* fast path */
278
0
  if (inc > 0) {
279
0
    hg_bucket_t *bp = key_to_bucket(hg, key);
280
0
    bp = bp != NULL ? bp : key_to_new_bucket(hg, key);
281
0
    atomic_fetch_add_relaxed(bp, inc);
282
0
  }
283
0
}
284
285
/**********************************************************************/
286
287
void
288
0
isc_histo_add(isc_histo_t *hg, uint64_t value, uint64_t inc) {
289
0
  REQUIRE(HISTO_VALID(hg));
290
0
  add_key_count(hg, value_to_key(hg, value), inc);
291
0
}
292
293
void
294
0
isc_histo_inc(isc_histo_t *hg, uint64_t value) {
295
0
  isc_histo_add(hg, value, 1);
296
0
}
297
298
void
299
0
isc_histo_put(isc_histo_t *hg, uint64_t min, uint64_t max, uint64_t count) {
300
0
  REQUIRE(HISTO_VALID(hg));
301
302
0
  unsigned int kmin = value_to_key(hg, min);
303
0
  unsigned int kmax = value_to_key(hg, max);
304
305
0
  for (unsigned int key = kmin; key <= kmax; key++) {
306
0
    uint64_t mid = ISC_MIN(max, key_to_maxval(hg, key));
307
0
    double in_bucket = mid - min + 1;
308
0
    double remaining = max - min + 1;
309
0
    uint64_t inc = ceil(count * in_bucket / remaining);
310
0
    add_key_count(hg, key, inc);
311
0
    count -= inc;
312
0
    min = mid + 1;
313
0
  }
314
0
}
315
316
isc_result_t
317
isc_histo_get(const isc_histo_t *hg, unsigned int key, uint64_t *minp,
318
0
        uint64_t *maxp, uint64_t *countp) {
319
0
  REQUIRE(HISTO_VALID(hg));
320
321
0
  if (key < BUCKETS(hg)) {
322
0
    SET_IF_NOT_NULL(minp, key_to_minval(hg, key));
323
0
    SET_IF_NOT_NULL(maxp, key_to_maxval(hg, key));
324
0
    SET_IF_NOT_NULL(countp, get_key_count(hg, key));
325
0
    return ISC_R_SUCCESS;
326
0
  } else {
327
0
    return ISC_R_RANGE;
328
0
  }
329
0
}
330
331
void
332
0
isc_histo_next(const isc_histo_t *hg, unsigned int *keyp) {
333
0
  REQUIRE(HISTO_VALID(hg));
334
0
  REQUIRE(keyp != NULL);
335
336
0
  unsigned int chunksize = CHUNKSIZE(hg);
337
0
  unsigned int buckets = BUCKETS(hg);
338
0
  unsigned int key = *keyp;
339
340
0
  key++;
341
0
  while (key < buckets && key % chunksize == 0 &&
342
0
         key_to_bucket(hg, key) == NULL)
343
0
  {
344
0
    key += chunksize;
345
0
  }
346
0
  *keyp = key;
347
0
}
348
349
void
350
0
isc_histo_merge(isc_histo_t **targetp, const isc_histo_t *source) {
351
0
  REQUIRE(HISTO_VALID(source));
352
0
  REQUIRE(targetp != NULL);
353
354
0
  if (*targetp != NULL) {
355
0
    REQUIRE(HISTO_VALID(*targetp));
356
0
  } else {
357
0
    isc_histo_create(source->mctx, source->sigbits, targetp);
358
0
  }
359
360
0
  uint64_t min, max, count;
361
0
  for (unsigned int key = 0;
362
0
       isc_histo_get(source, key, &min, &max, &count) == ISC_R_SUCCESS;
363
0
       isc_histo_next(source, &key))
364
0
  {
365
0
    isc_histo_put(*targetp, min, max, count);
366
0
  }
367
0
}
368
369
/**********************************************************************/
370
371
void
372
isc_histomulti_create(isc_mem_t *mctx, unsigned int sigbits,
373
0
          isc_histomulti_t **hmp) {
374
0
  REQUIRE(hmp != NULL);
375
0
  REQUIRE(*hmp == NULL);
376
377
0
  unsigned int size = isc_tid_count();
378
0
  INSIST(size > 0);
379
380
0
  isc_histomulti_t *hm = isc_mem_cget(mctx, 1,
381
0
              STRUCT_FLEX_SIZE(hm, hg, size));
382
0
  *hm = (isc_histomulti_t){
383
0
    .magic = HISTOMULTI_MAGIC,
384
0
    .size = size,
385
0
  };
386
387
0
  for (unsigned int i = 0; i < hm->size; i++) {
388
0
    isc_histo_create(mctx, sigbits, &hm->hg[i]);
389
0
  }
390
391
0
  isc_refcount_init(&hm->references, 1);
392
393
0
  *hmp = hm;
394
0
}
395
396
static void
397
0
isc__histomulti_destroy(isc_histomulti_t *hm) {
398
0
  REQUIRE(HISTOMULTI_VALID(hm));
399
400
0
  isc_mem_t *mctx = hm->hg[0]->mctx;
401
402
0
  for (unsigned int i = 0; i < hm->size; i++) {
403
0
    isc_histo_destroy(&hm->hg[i]);
404
0
  }
405
406
0
  isc_refcount_destroy(&hm->references);
407
408
0
  isc_mem_put(mctx, hm, STRUCT_FLEX_SIZE(hm, hg, hm->size));
409
0
}
410
411
void
412
0
isc_histomulti_merge(isc_histo_t **hgp, const isc_histomulti_t *hm) {
413
0
  REQUIRE(HISTOMULTI_VALID(hm));
414
415
0
  for (unsigned int i = 0; i < hm->size; i++) {
416
0
    isc_histo_merge(hgp, hm->hg[i]);
417
0
  }
418
0
}
419
420
void
421
0
isc_histomulti_add(isc_histomulti_t *hm, uint64_t value, uint64_t inc) {
422
0
  REQUIRE(HISTOMULTI_VALID(hm));
423
0
  isc_histo_t *hg = hm->hg[isc_tid()];
424
0
  add_key_count(hg, value_to_key(hg, value), inc);
425
0
}
426
427
void
428
0
isc_histomulti_inc(isc_histomulti_t *hm, uint64_t value) {
429
0
  isc_histomulti_add(hm, value, 1);
430
0
}
431
432
#ifdef ISC_HISTO_TRACE
433
ISC_REFCOUNT_TRACE_IMPL(isc_histomulti, isc__histomulti_destroy);
434
#else
435
0
ISC_REFCOUNT_IMPL(isc_histomulti, isc__histomulti_destroy);
Unexecuted instantiation: isc_histomulti_ref
Unexecuted instantiation: isc_histomulti_unref
Unexecuted instantiation: isc_histomulti_detach
436
0
#endif /* ISC_HISTO_TRACE */
437
0
438
0
/**********************************************************************/
439
0
440
0
/*
441
0
 * https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
442
0
 * equation 4 (incremental mean) and equation 44 (incremental variance)
443
0
 */
444
0
void
445
0
isc_histo_moments(const isc_histo_t *hg, double *pm0, double *pm1,
446
0
      double *pm2) {
447
0
  REQUIRE(HISTO_VALID(hg));
448
449
0
  uint64_t pop = 0;
450
0
  double mean = 0.0;
451
0
  double sigma = 0.0;
452
453
0
  uint64_t min, max, count;
454
0
  for (unsigned int key = 0;
455
0
       isc_histo_get(hg, key, &min, &max, &count) == ISC_R_SUCCESS;
456
0
       isc_histo_next(hg, &key))
457
0
  {
458
0
    if (count == 0) { /* avoid division by zero */
459
0
      continue;
460
0
    }
461
0
    double value = min / 2.0 + max / 2.0;
462
0
    double delta = value - mean;
463
0
    pop += count;
464
0
    mean += count * delta / pop;
465
0
    sigma += count * delta * (value - mean);
466
0
  }
467
468
0
  SET_IF_NOT_NULL(pm0, pop);
469
0
  SET_IF_NOT_NULL(pm1, mean);
470
0
  SET_IF_NOT_NULL(pm2, (pop > 0) ? sqrt(sigma / pop) : 0.0);
471
0
}
472
473
/*
474
 * Clamped linear interpolation
475
 *
476
 * `outrange` should be `((1 << n) - 1)` for some `n`; when `n` is larger
477
 * than 53, `outrange` can get rounded up to a power of 2, so we clamp the
478
 * result to keep within bounds (extra important when `max == UNSIGNED
479
 * INT64_MAX`)
480
 */
481
static inline uint64_t
482
0
lerp(uint64_t min, uint64_t max, uint64_t lo, uint64_t in, uint64_t hi) {
483
0
  double inrange = (double)(hi - lo);
484
0
  double inpart = (double)(in - lo);
485
0
  double outrange = (double)(max - min);
486
0
  double outpart = round(outrange * inpart / inrange);
487
0
  return min + ISC_MIN((uint64_t)outpart, max - min);
488
0
}
489
490
/*
491
 * There is non-zero space for the inner value, and it is inside the bounds
492
 */
493
static inline bool
494
0
inside(uint64_t lo, uint64_t in, uint64_t hi) {
495
0
  return lo < hi && lo <= in && in <= hi;
496
0
}
497
498
isc_result_t
499
isc_histo_quantiles(const isc_histo_t *hg, unsigned int size,
500
0
        const double *fraction, uint64_t *value) {
501
0
  hg_bucket_t *chunk[CHUNKS];
502
0
  uint64_t total[CHUNKS];
503
0
  uint64_t rank[ISC_HISTO_MAXQUANTILES];
504
505
0
  REQUIRE(HISTO_VALID(hg));
506
0
  REQUIRE(0 < size && size <= ISC_HISTO_MAXQUANTILES);
507
0
  REQUIRE(fraction != NULL);
508
0
  REQUIRE(value != NULL);
509
510
0
  const unsigned int maxchunk = MAXCHUNK(hg);
511
0
  const unsigned int chunksize = CHUNKSIZE(hg);
512
513
  /*
514
   * Find out which chunks exist and what their totals are. We take a
515
   * copy of the chunk pointers to reduce the need for atomic ops
516
   * later on. Scan from low to high so that higher buckets are more
517
   * likely to be in the CPU cache when we scan from high to low.
518
   */
519
0
  uint64_t population = 0;
520
0
  for (unsigned int c = 0; c < maxchunk; c++) {
521
0
    chunk[c] = get_chunk(hg, c);
522
0
    total[c] = 0;
523
0
    if (chunk[c] != NULL) {
524
0
      for (unsigned int b = chunksize; b-- > 0;) {
525
0
        total[c] += bucket_count(&chunk[c][b]);
526
0
      }
527
0
      population += total[c];
528
0
    }
529
0
  }
530
531
  /*
532
   * Now we know the population, we can convert fractions to ranks.
533
   * Also ensure they are within bounds and in decreasing order.
534
   */
535
0
  for (unsigned int i = 0; i < size; i++) {
536
0
    REQUIRE(0.0 <= fraction[i] && fraction[i] <= 1.0);
537
0
    REQUIRE(i == 0 || fraction[i - 1] > fraction[i]);
538
0
    rank[i] = round(fraction[i] * population);
539
0
  }
540
541
  /*
542
   * Scan chunks from high to low, keeping track of the bounds on
543
   * each chunk's ranks. Each time we match `rank[i]`, move on to the
544
   * next rank and continue the scan from the same place.
545
   */
546
0
  unsigned int i = 0;
547
0
  uint64_t chunk_lo = population;
548
0
  for (unsigned int c = maxchunk; c-- > 0;) {
549
0
    uint64_t chunk_hi = chunk_lo;
550
0
    chunk_lo = chunk_hi - total[c];
551
552
    /*
553
     * Scan buckets backwards within this chunk, in a similar
554
     * manner to the chunk scan. Skip all or part of the loop
555
     * if the current rank is not in the chunk.
556
     */
557
0
    uint64_t bucket_lo = chunk_hi;
558
0
    for (unsigned int b = chunksize;
559
0
         b-- > 0 && inside(chunk_lo, rank[i], chunk_hi);)
560
0
    {
561
0
      uint64_t bucket_hi = bucket_lo;
562
0
      bucket_lo = bucket_hi - bucket_count(&chunk[c][b]);
563
564
      /*
565
       * Convert all ranks that fall in this bucket.
566
       */
567
0
      while (inside(bucket_lo, rank[i], bucket_hi)) {
568
0
        unsigned int key = chunksize * c + b;
569
0
        value[i] = lerp(key_to_minval(hg, key),
570
0
            key_to_maxval(hg, key),
571
0
            bucket_lo, rank[i], bucket_hi);
572
0
        if (++i == size) {
573
0
          return ISC_R_SUCCESS;
574
0
        }
575
0
      }
576
0
    }
577
0
  }
578
579
0
  return ISC_R_UNSET;
580
0
}
581
582
/**********************************************************************/