mirror of https://github.com/postgres/postgres
This seems to offer significantly better search performance than the existing GiST opclass for inet/cidr, at least on data with a wide mix of network mask lengths. (That may suggest that the data splitting heuristics in the GiST opclass could be improved.) Emre Hasegeli, with mostly-cosmetic adjustments by me Discussion: <CAE2gYzxtth9qatW_OAqdOjykS0bxq7AYHLuyAQLPgT7H9ZU0Cw@mail.gmail.com>pull/31/head
parent
0fda682e54
commit
77e2906821
@ -0,0 +1,708 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* network_spgist.c |
||||||
|
* SP-GiST support for network types. |
||||||
|
* |
||||||
|
* We split inet index entries first by address family (IPv4 or IPv6). |
||||||
|
* If the entries below a given inner tuple are all of the same family, |
||||||
|
* we identify their common prefix and split by the next bit of the address, |
||||||
|
* and by whether their masklens exceed the length of the common prefix. |
||||||
|
* |
||||||
|
* An inner tuple that has both IPv4 and IPv6 children has a null prefix |
||||||
|
* and exactly two nodes, the first being for IPv4 and the second for IPv6. |
||||||
|
* |
||||||
|
* Otherwise, the prefix is a CIDR value representing the common prefix, |
||||||
|
* and there are exactly four nodes. Node numbers 0 and 1 are for addresses |
||||||
|
* with the same masklen as the prefix, while node numbers 2 and 3 are for |
||||||
|
* addresses with larger masklen. (We do not allow a tuple to contain |
||||||
|
* entries with masklen smaller than its prefix's.) Node numbers 0 and 1 |
||||||
|
* are distinguished by the next bit of the address after the common prefix, |
||||||
|
* and likewise for node numbers 2 and 3. If there are no more bits in |
||||||
|
* the address family, everything goes into node 0 (which will probably |
||||||
|
* lead to creating an allTheSame tuple). |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/backend/utils/adt/network_spgist.c |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "access/spgist.h" |
||||||
|
#include "catalog/pg_type.h" |
||||||
|
#include "utils/inet.h" |
||||||
|
|
||||||
|
|
||||||
|
static int inet_spg_node_number(const inet *val, int commonbits); |
||||||
|
static int inet_spg_consistent_bitmap(const inet *prefix, int nkeys, |
||||||
|
ScanKey scankeys, bool leaf); |
||||||
|
|
||||||
|
/*
|
||||||
|
* The SP-GiST configuration function |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
inet_spg_config(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
/* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */ |
||||||
|
spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); |
||||||
|
|
||||||
|
cfg->prefixType = CIDROID; |
||||||
|
cfg->labelType = VOIDOID; |
||||||
|
cfg->canReturnData = true; |
||||||
|
cfg->longValuesOK = false; |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The SP-GiST choose function |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
inet_spg_choose(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); |
||||||
|
spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); |
||||||
|
inet *val = DatumGetInetPP(in->datum), |
||||||
|
*prefix; |
||||||
|
int commonbits; |
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're looking at a tuple that splits by address family, choose the |
||||||
|
* appropriate subnode. |
||||||
|
*/ |
||||||
|
if (!in->hasPrefix) |
||||||
|
{ |
||||||
|
/* allTheSame isn't possible for such a tuple */ |
||||||
|
Assert(!in->allTheSame); |
||||||
|
Assert(in->nNodes == 2); |
||||||
|
|
||||||
|
out->resultType = spgMatchNode; |
||||||
|
out->result.matchNode.nodeN = (ip_family(val) == PGSQL_AF_INET) ? 0 : 1; |
||||||
|
out->result.matchNode.restDatum = InetPGetDatum(val); |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/* Else it must split by prefix */ |
||||||
|
Assert(in->nNodes == 4 || in->allTheSame); |
||||||
|
|
||||||
|
prefix = DatumGetInetPP(in->prefixDatum); |
||||||
|
commonbits = ip_bits(prefix); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We cannot put addresses from different families under the same inner |
||||||
|
* node, so we have to split if the new value's family is different. |
||||||
|
*/ |
||||||
|
if (ip_family(val) != ip_family(prefix)) |
||||||
|
{ |
||||||
|
/* Set up 2-node tuple */ |
||||||
|
out->resultType = spgSplitTuple; |
||||||
|
out->result.splitTuple.prefixHasPrefix = false; |
||||||
|
out->result.splitTuple.prefixNNodes = 2; |
||||||
|
out->result.splitTuple.prefixNodeLabels = NULL; |
||||||
|
|
||||||
|
/* Identify which node the existing data goes into */ |
||||||
|
out->result.splitTuple.childNodeN = |
||||||
|
(ip_family(prefix) == PGSQL_AF_INET) ? 0 : 1; |
||||||
|
|
||||||
|
out->result.splitTuple.postfixHasPrefix = true; |
||||||
|
out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix); |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* If the new value does not match the existing prefix, we have to split. |
||||||
|
*/ |
||||||
|
if (ip_bits(val) < commonbits || |
||||||
|
bitncmp(ip_addr(prefix), ip_addr(val), commonbits) != 0) |
||||||
|
{ |
||||||
|
/* Determine new prefix length for the split tuple */ |
||||||
|
commonbits = bitncommon(ip_addr(prefix), ip_addr(val), |
||||||
|
Min(ip_bits(val), commonbits)); |
||||||
|
|
||||||
|
/* Set up 4-node tuple */ |
||||||
|
out->resultType = spgSplitTuple; |
||||||
|
out->result.splitTuple.prefixHasPrefix = true; |
||||||
|
out->result.splitTuple.prefixPrefixDatum = |
||||||
|
InetPGetDatum(cidr_set_masklen_internal(val, commonbits)); |
||||||
|
out->result.splitTuple.prefixNNodes = 4; |
||||||
|
out->result.splitTuple.prefixNodeLabels = NULL; |
||||||
|
|
||||||
|
/* Identify which node the existing data goes into */ |
||||||
|
out->result.splitTuple.childNodeN = |
||||||
|
inet_spg_node_number(prefix, commonbits); |
||||||
|
|
||||||
|
out->result.splitTuple.postfixHasPrefix = true; |
||||||
|
out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix); |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* All OK, choose the node to descend into. (If this tuple is marked |
||||||
|
* allTheSame, the core code will ignore our choice of nodeN; but we need |
||||||
|
* not account for that case explicitly here.) |
||||||
|
*/ |
||||||
|
out->resultType = spgMatchNode; |
||||||
|
out->result.matchNode.nodeN = inet_spg_node_number(val, commonbits); |
||||||
|
out->result.matchNode.restDatum = InetPGetDatum(val); |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The GiST PickSplit method |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
inet_spg_picksplit(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); |
||||||
|
spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); |
||||||
|
inet *prefix, |
||||||
|
*tmp; |
||||||
|
int i, |
||||||
|
commonbits; |
||||||
|
bool differentFamilies = false; |
||||||
|
|
||||||
|
/* Initialize the prefix with the first item */ |
||||||
|
prefix = DatumGetInetPP(in->datums[0]); |
||||||
|
commonbits = ip_bits(prefix); |
||||||
|
|
||||||
|
/* Examine remaining items to discover minimum common prefix length */ |
||||||
|
for (i = 1; i < in->nTuples; i++) |
||||||
|
{ |
||||||
|
tmp = DatumGetInetPP(in->datums[i]); |
||||||
|
|
||||||
|
if (ip_family(tmp) != ip_family(prefix)) |
||||||
|
{ |
||||||
|
differentFamilies = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (ip_bits(tmp) < commonbits) |
||||||
|
commonbits = ip_bits(tmp); |
||||||
|
commonbits = bitncommon(ip_addr(prefix), ip_addr(tmp), commonbits); |
||||||
|
if (commonbits == 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Don't need labels; allocate output arrays */ |
||||||
|
out->nodeLabels = NULL; |
||||||
|
out->mapTuplesToNodes = (int *) palloc(sizeof(int) * in->nTuples); |
||||||
|
out->leafTupleDatums = (Datum *) palloc(sizeof(Datum) * in->nTuples); |
||||||
|
|
||||||
|
if (differentFamilies) |
||||||
|
{ |
||||||
|
/* Set up 2-node tuple */ |
||||||
|
out->hasPrefix = false; |
||||||
|
out->nNodes = 2; |
||||||
|
|
||||||
|
for (i = 0; i < in->nTuples; i++) |
||||||
|
{ |
||||||
|
tmp = DatumGetInetPP(in->datums[i]); |
||||||
|
out->mapTuplesToNodes[i] = |
||||||
|
(ip_family(tmp) == PGSQL_AF_INET) ? 0 : 1; |
||||||
|
out->leafTupleDatums[i] = InetPGetDatum(tmp); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* Set up 4-node tuple */ |
||||||
|
out->hasPrefix = true; |
||||||
|
out->prefixDatum = |
||||||
|
InetPGetDatum(cidr_set_masklen_internal(prefix, commonbits)); |
||||||
|
out->nNodes = 4; |
||||||
|
|
||||||
|
for (i = 0; i < in->nTuples; i++) |
||||||
|
{ |
||||||
|
tmp = DatumGetInetPP(in->datums[i]); |
||||||
|
out->mapTuplesToNodes[i] = inet_spg_node_number(tmp, commonbits); |
||||||
|
out->leafTupleDatums[i] = InetPGetDatum(tmp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The SP-GiST query consistency check for inner tuples |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
inet_spg_inner_consistent(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); |
||||||
|
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); |
||||||
|
int i; |
||||||
|
int which; |
||||||
|
|
||||||
|
if (!in->hasPrefix) |
||||||
|
{ |
||||||
|
Assert(!in->allTheSame); |
||||||
|
Assert(in->nNodes == 2); |
||||||
|
|
||||||
|
/* Identify which child nodes need to be visited */ |
||||||
|
which = 1 | (1 << 1); |
||||||
|
|
||||||
|
for (i = 0; i < in->nkeys; i++) |
||||||
|
{ |
||||||
|
StrategyNumber strategy = in->scankeys[i].sk_strategy; |
||||||
|
inet *argument = DatumGetInetPP(in->scankeys[i].sk_argument); |
||||||
|
|
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (ip_family(argument) == PGSQL_AF_INET) |
||||||
|
which &= 1; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (ip_family(argument) == PGSQL_AF_INET6) |
||||||
|
which &= (1 << 1); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
/* all other ops can only match addrs of same family */ |
||||||
|
if (ip_family(argument) == PGSQL_AF_INET) |
||||||
|
which &= 1; |
||||||
|
else |
||||||
|
which &= (1 << 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else if (!in->allTheSame) |
||||||
|
{ |
||||||
|
Assert(in->nNodes == 4); |
||||||
|
|
||||||
|
/* Identify which child nodes need to be visited */ |
||||||
|
which = inet_spg_consistent_bitmap(DatumGetInetPP(in->prefixDatum), |
||||||
|
in->nkeys, in->scankeys, false); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* Must visit all nodes; we assume there are less than 32 of 'em */ |
||||||
|
which = ~0; |
||||||
|
} |
||||||
|
|
||||||
|
out->nNodes = 0; |
||||||
|
|
||||||
|
if (which) |
||||||
|
{ |
||||||
|
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); |
||||||
|
|
||||||
|
for (i = 0; i < in->nNodes; i++) |
||||||
|
{ |
||||||
|
if (which & (1 << i)) |
||||||
|
{ |
||||||
|
out->nodeNumbers[out->nNodes] = i; |
||||||
|
out->nNodes++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The SP-GiST query consistency check for leaf tuples |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
inet_spg_leaf_consistent(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); |
||||||
|
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); |
||||||
|
inet *leaf = DatumGetInetPP(in->leafDatum); |
||||||
|
|
||||||
|
/* All tests are exact. */ |
||||||
|
out->recheck = false; |
||||||
|
|
||||||
|
/* Leaf is what it is... */ |
||||||
|
out->leafValue = InetPGetDatum(leaf); |
||||||
|
|
||||||
|
/* Use common code to apply the tests. */ |
||||||
|
PG_RETURN_BOOL(inet_spg_consistent_bitmap(leaf, in->nkeys, in->scankeys, |
||||||
|
true)); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate node number (within a 4-node, single-family inner index tuple) |
||||||
|
* |
||||||
|
* The value must have the same family as the node's prefix, and |
||||||
|
* commonbits is the mask length of the prefix. We use even or odd |
||||||
|
* nodes according to the next address bit after the commonbits, |
||||||
|
* and low or high nodes according to whether the value's mask length |
||||||
|
* is larger than commonbits. |
||||||
|
*/ |
||||||
|
static int |
||||||
|
inet_spg_node_number(const inet *val, int commonbits) |
||||||
|
{ |
||||||
|
int nodeN = 0; |
||||||
|
|
||||||
|
if (commonbits < ip_maxbits(val) && |
||||||
|
ip_addr(val)[commonbits / 8] & (1 << (7 - commonbits % 8))) |
||||||
|
nodeN |= 1; |
||||||
|
if (commonbits < ip_bits(val)) |
||||||
|
nodeN |= 2; |
||||||
|
|
||||||
|
return nodeN; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate bitmap of node numbers that are consistent with the query |
||||||
|
* |
||||||
|
* This can be used either at a 4-way inner tuple, or at a leaf tuple. |
||||||
|
* In the latter case, we should return a boolean result (0 or 1) |
||||||
|
* not a bitmap. |
||||||
|
* |
||||||
|
* This definition is pretty odd, but the inner and leaf consistency checks |
||||||
|
* are mostly common and it seems best to keep them in one function. |
||||||
|
*/ |
||||||
|
static int |
||||||
|
inet_spg_consistent_bitmap(const inet *prefix, int nkeys, ScanKey scankeys, |
||||||
|
bool leaf) |
||||||
|
{ |
||||||
|
int bitmap; |
||||||
|
int commonbits, |
||||||
|
i; |
||||||
|
|
||||||
|
/* Initialize result to allow visiting all children */ |
||||||
|
if (leaf) |
||||||
|
bitmap = 1; |
||||||
|
else |
||||||
|
bitmap = 1 | (1 << 1) | (1 << 2) | (1 << 3); |
||||||
|
|
||||||
|
commonbits = ip_bits(prefix); |
||||||
|
|
||||||
|
for (i = 0; i < nkeys; i++) |
||||||
|
{ |
||||||
|
inet *argument = DatumGetInetPP(scankeys[i].sk_argument); |
||||||
|
StrategyNumber strategy = scankeys[i].sk_strategy; |
||||||
|
int order; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 0: different families |
||||||
|
* |
||||||
|
* Matching families do not help any of the strategies. |
||||||
|
*/ |
||||||
|
if (ip_family(argument) != ip_family(prefix)) |
||||||
|
{ |
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (ip_family(argument) < ip_family(prefix)) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (ip_family(argument) > ip_family(prefix)) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
/* For all other cases, we can be sure there is no match */ |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
|
||||||
|
/* Other checks make no sense with different families. */ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 1: network bit count |
||||||
|
* |
||||||
|
* Network bit count (ip_bits) helps to check leaves for sub network |
||||||
|
* and sup network operators. At non-leaf nodes, we know every child |
||||||
|
* value has greater ip_bits, so we can avoid descending in some cases |
||||||
|
* too. |
||||||
|
* |
||||||
|
* This check is less expensive than checking the address bits, so we |
||||||
|
* are doing this before, but it has to be done after for the basic |
||||||
|
* comparison strategies, because ip_bits only affect their results |
||||||
|
* when the common network bits are the same. |
||||||
|
*/ |
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTSubStrategyNumber: |
||||||
|
if (commonbits <= ip_bits(argument)) |
||||||
|
bitmap &= (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTSubEqualStrategyNumber: |
||||||
|
if (commonbits < ip_bits(argument)) |
||||||
|
bitmap &= (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTSuperStrategyNumber: |
||||||
|
if (commonbits == ip_bits(argument) - 1) |
||||||
|
bitmap &= 1 | (1 << 1); |
||||||
|
else if (commonbits >= ip_bits(argument)) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTSuperEqualStrategyNumber: |
||||||
|
if (commonbits == ip_bits(argument)) |
||||||
|
bitmap &= 1 | (1 << 1); |
||||||
|
else if (commonbits > ip_bits(argument)) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTEqualStrategyNumber: |
||||||
|
if (commonbits < ip_bits(argument)) |
||||||
|
bitmap &= (1 << 2) | (1 << 3); |
||||||
|
else if (commonbits == ip_bits(argument)) |
||||||
|
bitmap &= 1 | (1 << 1); |
||||||
|
else |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 2: common network bits |
||||||
|
* |
||||||
|
* Compare available common prefix bits to the query, but not beyond |
||||||
|
* either the query's netmask or the minimum netmask among the |
||||||
|
* represented values. If these bits don't match the query, we can |
||||||
|
* eliminate some cases. |
||||||
|
*/ |
||||||
|
order = bitncmp(ip_addr(prefix), ip_addr(argument), |
||||||
|
Min(commonbits, ip_bits(argument))); |
||||||
|
|
||||||
|
if (order != 0) |
||||||
|
{ |
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (order > 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (order < 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
/* For all other cases, we can be sure there is no match */ |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Remaining checks make no sense when common bits don't match. |
||||||
|
*/ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 3: next network bit |
||||||
|
* |
||||||
|
* We can filter out branch 2 or 3 using the next network bit of the |
||||||
|
* argument, if it is available. |
||||||
|
* |
||||||
|
* This check matters for the performance of the search. The results |
||||||
|
* would be correct without it. |
||||||
|
*/ |
||||||
|
if (bitmap & ((1 << 2) | (1 << 3)) && |
||||||
|
commonbits < ip_bits(argument)) |
||||||
|
{ |
||||||
|
int nextbit; |
||||||
|
|
||||||
|
nextbit = ip_addr(argument)[commonbits / 8] & |
||||||
|
(1 << (7 - commonbits % 8)); |
||||||
|
|
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (!nextbit) |
||||||
|
bitmap &= 1 | (1 << 1) | (1 << 2); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (nextbit) |
||||||
|
bitmap &= 1 | (1 << 1) | (1 << 3); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
if (!nextbit) |
||||||
|
bitmap &= 1 | (1 << 1) | (1 << 2); |
||||||
|
else |
||||||
|
bitmap &= 1 | (1 << 1) | (1 << 3); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Remaining checks are only for the basic comparison strategies. This |
||||||
|
* test relies on the strategy number ordering defined in stratnum.h. |
||||||
|
*/ |
||||||
|
if (strategy < RTEqualStrategyNumber || |
||||||
|
strategy > RTGreaterEqualStrategyNumber) |
||||||
|
continue; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 4: network bit count |
||||||
|
* |
||||||
|
* At this point, we know that the common network bits of the prefix |
||||||
|
* and the argument are the same, so we can go forward and check the |
||||||
|
* ip_bits. |
||||||
|
*/ |
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (commonbits == ip_bits(argument)) |
||||||
|
bitmap &= 1 | (1 << 1); |
||||||
|
else if (commonbits > ip_bits(argument)) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (commonbits < ip_bits(argument)) |
||||||
|
bitmap &= (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
|
||||||
|
/* Remaining checks don't make sense with different ip_bits. */ |
||||||
|
if (commonbits != ip_bits(argument)) |
||||||
|
continue; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 5: next host bit |
||||||
|
* |
||||||
|
* We can filter out branch 0 or 1 using the next host bit of the |
||||||
|
* argument, if it is available. |
||||||
|
* |
||||||
|
* This check matters for the performance of the search. The results |
||||||
|
* would be correct without it. There is no point in running it for |
||||||
|
* leafs as we have to check the whole address on the next step. |
||||||
|
*/ |
||||||
|
if (!leaf && bitmap & (1 | (1 << 1)) && |
||||||
|
commonbits < ip_maxbits(argument)) |
||||||
|
{ |
||||||
|
int nextbit; |
||||||
|
|
||||||
|
nextbit = ip_addr(argument)[commonbits / 8] & |
||||||
|
(1 << (7 - commonbits % 8)); |
||||||
|
|
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (!nextbit) |
||||||
|
bitmap &= 1 | (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (nextbit) |
||||||
|
bitmap &= (1 << 1) | (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
if (!nextbit) |
||||||
|
bitmap &= 1 | (1 << 2) | (1 << 3); |
||||||
|
else |
||||||
|
bitmap &= (1 << 1) | (1 << 2) | (1 << 3); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check 6: whole address |
||||||
|
* |
||||||
|
* This is the last check for correctness of the basic comparison |
||||||
|
* strategies. It's only appropriate at leaf entries. |
||||||
|
*/ |
||||||
|
if (leaf) |
||||||
|
{ |
||||||
|
/* Redo ordering comparison using all address bits */ |
||||||
|
order = bitncmp(ip_addr(prefix), ip_addr(argument), |
||||||
|
ip_maxbits(prefix)); |
||||||
|
|
||||||
|
switch (strategy) |
||||||
|
{ |
||||||
|
case RTLessStrategyNumber: |
||||||
|
if (order >= 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTLessEqualStrategyNumber: |
||||||
|
if (order > 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTEqualStrategyNumber: |
||||||
|
if (order != 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterEqualStrategyNumber: |
||||||
|
if (order < 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTGreaterStrategyNumber: |
||||||
|
if (order <= 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case RTNotEqualStrategyNumber: |
||||||
|
if (order == 0) |
||||||
|
bitmap = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (!bitmap) |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return bitmap; |
||||||
|
} |
Loading…
Reference in new issue