haproxy/ebtree/eb32sctree.h
Willy Tarreau 41136de58e BUILD: ebtree: improve architecture-specific alignment
Commit 2c315ee75e ("BUG/MEDIUM: ebtree: don't set attribute packed
without unaligned access support") addressed alignment issues in
ebtrees in a way that is not really optimal since it will leave holes
in eb32trees for example.

This fix is better in that it restores the packed attribute on ebnode
but enforces proper alignment on the carrying nodes where necessary.
This also has the benefit of closing holes wherever possible and to
align data to the minimally required size.

The only thing it cannot close is the 32-bit hole at the end of ebmbnode
due to the required 64-bit on certain archs but at least it guarantees
that the key correctly points to the end of the node and that there is
never a hole after it.

This is a better fix than the one above and should be backported to
branches where the one above will be backported.
2020-02-25 10:34:49 +01:00

146 lines
5.1 KiB
C

/*
* Elastic Binary Trees - macros and structures for operations on 32bit nodes.
* Version 6.0.6 with backports from v7-dev
* (C) 2002-2017 - Willy Tarreau <w@1wt.eu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _EB32SCTREE_H
#define _EB32SCTREE_H
#include "ebtree.h"
/* Return the structure of type <type> whose member <member> points to <ptr> */
#define eb32sc_entry(ptr, type, member) container_of(ptr, type, member)
/* These types may sometimes already be defined */
#ifndef _EB32TREE_H
typedef unsigned int u32;
typedef signed int s32;
#endif
/* This structure carries a node, a leaf, a scope, and a key. It must start
* with the eb_node so that it can be cast into an eb_node. We could also
* have put some sort of transparent union here to reduce the indirection
* level, but the fact is, the end user is not meant to manipulate internals,
* so this is pointless.
* In case sizeof(void*)>=sizeof(long), we know there will be some padding after
* the leaf if it's unaligned. In this case we force the alignment on void* so
* that we prefer to have the padding before for more efficient accesses.
*/
struct eb32sc_node {
struct eb_node node; /* the tree node, must be at the beginning */
MAYBE_ALIGN(sizeof(u32));
u32 key;
ALWAYS_ALIGN(sizeof(void*));
unsigned long node_s; /* visibility of this node's branches */
unsigned long leaf_s; /* visibility of this node's leaf */
} ALIGNED(sizeof(void*));
/*
* Exported functions and macros.
* Many of them are always inlined because they are extremely small, and
* are generally called at most once or twice in a program.
*/
/*
* The following functions are not inlined by default. They are declared
* in eb32sctree.c, which simply relies on their inline version.
*/
struct eb32sc_node *eb32sc_lookup_ge(struct eb_root *root, u32 x, unsigned long scope);
struct eb32sc_node *eb32sc_lookup_ge_or_first(struct eb_root *root, u32 x, unsigned long scope);
struct eb32sc_node *eb32sc_insert(struct eb_root *root, struct eb32sc_node *new, unsigned long scope);
void eb32sc_delete(struct eb32sc_node *node);
/* Walks down left starting at root pointer <start>, and follow the leftmost
* branch whose scope matches <scope>. It either returns the node hosting the
* first leaf on that side, or NULL if no leaf is found. <start> may either be
* NULL or a branch pointer. The pointer to the leaf (or NULL) is returned.
*/
static inline struct eb32sc_node *eb32sc_walk_down_left(eb_troot_t *start, unsigned long scope)
{
struct eb_root *root;
struct eb_node *node;
struct eb32sc_node *eb32;
if (unlikely(!start))
return NULL;
while (1) {
if (eb_gettag(start) == EB_NODE) {
root = eb_untag(start, EB_NODE);
node = eb_root_to_node(root);
eb32 = container_of(node, struct eb32sc_node, node);
if (eb32->node_s & scope) {
start = node->branches.b[EB_LEFT];
continue;
}
start = node->node_p;
}
else {
root = eb_untag(start, EB_LEAF);
node = eb_root_to_node(root);
eb32 = container_of(node, struct eb32sc_node, node);
if (eb32->leaf_s & scope)
return eb32;
start = node->leaf_p;
}
/* here we're on a node that doesn't match the scope. We have
* to walk to the closest right location.
*/
while (eb_gettag(start) != EB_LEFT)
/* Walking up from right branch, so we cannot be below root */
start = (eb_root_to_node(eb_untag(start, EB_RGHT)))->node_p;
/* Note that <start> cannot be NULL at this stage */
root = eb_untag(start, EB_LEFT);
start = root->b[EB_RGHT];
if (eb_clrtag(start) == NULL)
return NULL;
}
}
/* Return next node in the tree, starting with tagged parent <start>, or NULL if none */
static inline struct eb32sc_node *eb32sc_next_with_parent(eb_troot_t *start, unsigned long scope)
{
while (eb_gettag(start) != EB_LEFT)
/* Walking up from right branch, so we cannot be below root */
start = (eb_root_to_node(eb_untag(start, EB_RGHT)))->node_p;
/* Note that <t> cannot be NULL at this stage */
start = (eb_untag(start, EB_LEFT))->b[EB_RGHT];
if (eb_clrtag(start) == NULL)
return NULL;
return eb32sc_walk_down_left(start, scope);
}
/* Return next node in the tree, or NULL if none */
static inline struct eb32sc_node *eb32sc_next(struct eb32sc_node *eb32, unsigned long scope)
{
return eb32sc_next_with_parent(eb32->node.leaf_p, scope);
}
/* Return leftmost node in the tree, or NULL if none */
static inline struct eb32sc_node *eb32sc_first(struct eb_root *root, unsigned long scope)
{
return eb32sc_walk_down_left(root->b[0], scope);
}
#endif /* _EB32SC_TREE_H */