bcachefs-tools/libbcache/eytzinger.h
2017-01-20 09:07:08 -09:00

197 lines
3.8 KiB
C

#ifndef _EYTZINGER_H
#define _EYTZINGER_H
#include <linux/bitops.h>
#include <linux/log2.h>
#include "util.h"
/*
* Traversal for trees in eytzinger layout - a full binary tree layed out in an
* array
*
* We used one based indexing, not zero based: with one based indexing, each
* level of the tree starts at a power of two - leading to better alignment -
* and it's what you want for implementing next/prev and to/from inorder.
*
* To/from inorder also uses 1 based indexing.
*
* Size parameter is treated as if we were using 0 based indexing, however:
* valid nodes, and inorder indices, are in the range [1..size)
*/
static inline unsigned eytzinger_child(unsigned j, unsigned child)
{
EBUG_ON(child > 1);
return (j << 1) + child;
}
static inline unsigned eytzinger_left_child(unsigned j)
{
return eytzinger_child(j, 0);
}
static inline unsigned eytzinger_right_child(unsigned j)
{
return eytzinger_child(j, 1);
}
static inline unsigned eytzinger_first(unsigned size)
{
return rounddown_pow_of_two(size - 1);
}
static inline unsigned eytzinger_last(unsigned size)
{
return rounddown_pow_of_two(size) - 1;
}
/*
* eytzinger_next() and eytzinger_prev() have the nice properties that
*
* eytzinger_next(0) == eytzinger_first())
* eytzinger_prev(0) == eytzinger_last())
*
* eytzinger_prev(eytzinger_first()) == 0
* eytzinger_next(eytzinger_last()) == 0
*/
static inline unsigned eytzinger_next(unsigned j, unsigned size)
{
EBUG_ON(j >= size);
if (eytzinger_right_child(j) < size) {
j = eytzinger_right_child(j);
j <<= __fls(size) - __fls(j);
j >>= j >= size;
} else {
j >>= ffz(j) + 1;
}
return j;
}
static inline unsigned eytzinger_prev(unsigned j, unsigned size)
{
EBUG_ON(j >= size);
if (eytzinger_left_child(j) < size) {
j = eytzinger_left_child(j);
j <<= __fls(size) - __fls(j);
j -= 1;
j >>= j >= size;
} else {
j >>= __ffs(j) + 1;
}
return j;
}
static inline unsigned eytzinger_extra(unsigned size)
{
return (size - rounddown_pow_of_two(size - 1)) << 1;
}
static inline unsigned __eytzinger_to_inorder(unsigned j, unsigned size,
unsigned extra)
{
unsigned b = __fls(j);
unsigned shift = __fls(size - 1) - b;
int s;
EBUG_ON(!j || j >= size);
j ^= 1U << b;
j <<= 1;
j |= 1;
j <<= shift;
/*
* sign bit trick:
*
* if (j > extra)
* j -= (j - extra) >> 1;
*/
s = extra - j;
j += (s >> 1) & (s >> 31);
return j;
}
static inline unsigned __inorder_to_eytzinger(unsigned j, unsigned size,
unsigned extra)
{
unsigned shift;
int s;
EBUG_ON(!j || j >= size);
/*
* sign bit trick:
*
* if (j > extra)
* j += j - extra;
*/
s = extra - j;
j -= s & (s >> 31);
shift = __ffs(j);
j >>= shift + 1;
j |= 1U << (__fls(size - 1) - shift);
return j;
}
static inline unsigned eytzinger_to_inorder(unsigned j, unsigned size)
{
return __eytzinger_to_inorder(j, size, eytzinger_extra(size));
}
static inline unsigned inorder_to_eytzinger(unsigned j, unsigned size)
{
return __inorder_to_eytzinger(j, size, eytzinger_extra(size));
}
#define eytzinger_for_each(_i, _size) \
for ((_i) = eytzinger_first((_size)); \
(_i) != 0; \
(_i) = eytzinger_next((_i), (_size)))
#if 0
void eytzinger_test(void)
{
unsigned i, j, size;
for (size = 2;
size < 65536000;
size++) {
if (!(size % 4096))
printk(KERN_INFO "tree size %u\n", size);
assert(eytzinger_prev(0, size) == eytzinger_last(size));
assert(eytzinger_next(0, size) == eytzinger_first(size));
assert(eytzinger_prev(eytzinger_first(size), size) == 0);
assert(eytzinger_next(eytzinger_last(size), size) == 0);
eytzinger_for_each(j, size) {
assert(from_inorder(i, size) == j);
assert(to_inorder(j, size) == i);
if (j != eytzinger_last(size)) {
unsigned next = eytzinger_next(j, size);
assert(eytzinger_prev(next, size) == j);
}
}
}
}
#endif
#endif /* _EYTZINGER_H */