/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ipp/ipgpc/trie.h>
#include <ipp/ipgpc/filters.h>
#include <ipp/ipgpc/classifier.h>
#include <inet/ip6.h>
/* trie data structure used for classifying IP addresses and TCP/UDP ports */
#define ZERO 0
#define ONE 1
/* Statics */
static void t_split(node_t **, uint8_t, uint8_t);
static boolean_t t_traverse_delete(node_t **, uint8_t, key_t, uint32_t,
uint32_t, trie_id_t **);
/*
* create_node(flag)
*
* generates a pointer to a new trie node
* flag is passed to kmem_alloc
* returns NULL to signify memory error
*/
node_t *
create_node(int flag)
{
node_t *buf = kmem_cache_alloc(trie_node_cache, flag);
if (buf == NULL) {
return (NULL);
}
buf->elements = NULL;
buf->zero = NULL;
buf->one = NULL;
buf->pos = 0;
buf->bits = 0;
buf->val = 0;
buf->mask = 0;
buf->isroot = 0;
return (buf);
}
/*
* t_split(c_node, pos, key_len)
*
* performs a split on c_node for the following three cases:
* 1 a mismatch occured between the insert key and the value at the node
* 2 the insert key specifies a shorter key than the one at the node
* 3 the insert key specifies a longer key than the one at the node
* cases 1 and 2 are handled in the same way
* when t_split returns, c_node->one and c_node->zero must != NULL
*
* (note: we assume a key_len = n (where in the real world n = 16 | 32),
* and a "key" in this example is actaully some value of key_len n that
* has its high order bits masked.
* For example: key = 1011 with key_len = 8, would actaully be the key:mask
* combo 1011xxxx:11110000. I am using short keys for ease of example)
* Case 1 and 2:
*
* assume 8 bit keys for all examples
*
* trie A contains keys 111011, 0, 10
* *
* / \
* *
* / \
* * * bits = 4 pos = 5 val = 1011 mask = 00111100
* inserting 111100 would result in the following split
* *
* / \
* *
* / \
* * bits = 1 pos = 5 val = 1 mask = 00100000
* / \
* bits = 2 pos = 3 val=11* * (to be inserted: (bits = 2 pos = 3 val = 00
* mask = 00001100 mask = 00001100))
*
* Case 3:
*
* trie A same as above, before insert
* inserting key 11101111 would results in the following split
* *
* / \
* *
* / \
* * * bits = 4 pos = 5 val = 1011 mask = 00111100
* / \
* * * (to be inserted: bits = 1 pos = 0 val = 1 mask = 00000001)
*/
/* ARGSUSED */
static void
t_split(node_t **c_node, uint8_t pos, uint8_t key_len)
{
uint8_t old_bits = 0;
uint8_t i;
int bit;
node_t *nodep = *c_node;
node_t *tnodep = NULL;
/* check if case is that the mask is longer */
if (pos == (nodep->pos - nodep->bits)) {
/* pos is past the last bit covered at this node */
ASSERT(nodep->one == NULL);
ASSERT(nodep->zero == NULL);
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else { /* pos > (nodep->pos - nodep->bits) */
old_bits = nodep->bits; /* save old bits entry */
/* nodep->pos will remain the same */
nodep->bits = nodep->pos - pos;
/* find the mismatch bit */
bit = EXTRACTBIT(nodep->val, pos, key_len);
if (bit == ZERO) {
if ((nodep->one == NULL) && (nodep->zero == NULL)) {
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else {
tnodep = create_node(KM_SLEEP);
tnodep->one = nodep->one;
tnodep->zero = nodep->zero;
nodep->zero = tnodep;
nodep->one = create_node(KM_SLEEP);
}
/* pos is before the last bit covered at this node */
nodep->zero->pos = pos - 1; /* link is one bit */
/* bits gets remaining bits minus the link */
nodep->zero->bits = (old_bits - nodep->bits) - 1;
/* set bits that are covered by this node */
for (i = 0; i < nodep->zero->bits; ++i) {
SETBIT(nodep->zero->val,
(nodep->zero->pos - i),
EXTRACTBIT(nodep->val,
(nodep->zero->pos - i), key_len),
key_len);
SETBIT(nodep->zero->mask,
(nodep->zero->pos - i), 1, key_len);
}
nodep->zero->elements = nodep->elements;
nodep->elements = NULL;
} else { /* bit == ONE */
if ((nodep->one == NULL) && (nodep->zero == NULL)) {
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else {
tnodep = create_node(KM_SLEEP);
tnodep->one = nodep->one;
tnodep->zero = nodep->zero;
nodep->one = tnodep;
nodep->zero = create_node(KM_SLEEP);
}
/* pos is before the last bit covered at this node */
nodep->one->pos = pos - 1; /* link is one bit */
/* bits gets remaining bits minus the link */
nodep->one->bits = (old_bits - nodep->bits) - 1;
/* set bits that are covered by this node */
for (i = 0; i < nodep->one->bits; ++i) {
SETBIT(nodep->one->val, (nodep->one->pos - i),
EXTRACTBIT(nodep->val,
(nodep->one->pos - i), key_len),
key_len);
SETBIT(nodep->one->mask,
(nodep->one->pos - i), 1, key_len);
}
nodep->one->elements = nodep->elements;
nodep->elements = NULL;
}
/* clear bits no longer covered by this node, from pos=>0 */
for (i = 0; i <= pos; ++i) {
UNSETBIT(nodep->val, i, key_len);
UNSETBIT(nodep->mask, i, key_len);
}
}
}
/*
* t_insert(tid, id, key, mask)
*
* inserts a new value, id, into the trie, tid->trie with the input key
* - if node exists, id is appended to element list at the node, if id does
* not already exist.
* - if node does not exist, a new node is created and id is the head of a new
* element list
* return DONTCARE_VALUE if mask == 0, otherwise NORMAL_VALUE
*/
int
t_insert(trie_id_t *tid, key_t id, uint32_t key, uint32_t mask)
{
node_t *c_node;
int bit;
uint8_t pos;
uint8_t key_len = (uint8_t)tid->key_len;
/* don't insert if don't care */
if (mask == 0) {
++tid->stats.num_dontcare;
return (DONTCARE_VALUE);
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie; /* point at trie root */
key &= mask; /* apply mask */
/* traverse trie to the correct position */
for (pos = key_len; pos > 0; --pos) {
/* check if bit is significant */
/* bit in key is significant if it is covered by the mask */
if (EXTRACTBIT(mask, (pos - 1), key_len) != 1) {
/* check if this is a path compressed internal node */
if (c_node->bits > 0) {
/* check if split is needed */
if ((pos - 1) > (c_node->pos - c_node->bits)) {
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
}
}
break;
}
/* extra bit at current position */
bit = EXTRACTBIT(key, (pos - 1), key_len);
/* check if this is a path compressed internal node */
if (c_node->bits > 0) { /* path compressed node */
/* check if split is needed */
if ((pos - 1) > (c_node->pos - c_node->bits)) {
/* testing for mismatch */
if (bit != EXTRACTBIT(c_node->val, (pos - 1),
key_len)) {
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
} else {
continue; /* bits match, so go on */
}
} else if ((pos - 1) == (c_node->pos - c_node->bits)) {
/* check if at a leaf node with elements */
if ((c_node->one == NULL) &&
(c_node->zero == NULL) &&
(c_node->elements != NULL)) {
/*
* this case occurs when mask for key
* is longer than mask for key at
* current node
*/
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
}
} /* else continue onto child */
}
if (bit == ZERO) {
if (c_node->zero == NULL) { /* leaf node */
if (c_node->bits == 0) {
c_node->pos = (pos - 1);
}
c_node->bits++;
/* bit at pos for node value should be 0 */
UNSETBIT(c_node->val, (pos - 1), key_len);
SETBIT(c_node->mask, (pos - 1), 1, key_len);
} else {
/* assert that trie is path compressed */
ASSERT(c_node->one != NULL);
c_node = c_node->zero; /* internal node */
}
} else { /* ONE bit */
if (c_node->one == NULL) { /* leaf node */
if (c_node->bits == 0) {
c_node->pos = (pos - 1);
}
c_node->bits++;
/* bit at pos for node value should be 1 */
SETBIT(c_node->val, (pos - 1), 1, key_len);
SETBIT(c_node->mask, (pos - 1), 1, key_len);
} else {
/* assert that trie is path compressed */
ASSERT(c_node->zero != NULL);
c_node = c_node->one; /* internal node */
}
}
}
/* insert at node */
(void) ipgpc_list_insert(&c_node->elements, id);
/* update stats */
++tid->stats.num_inserted;
/*
* check if this is the first key to be inserted that is not a
* don't care (*)
*/
if (tid->info.dontcareonly == B_TRUE) {
tid->info.dontcareonly = B_FALSE;
}
rw_exit(&tid->rw_lock);
return (NORMAL_VALUE);
}
/*
* t_insert6(tid, id, key, mask)
*
* specific to inserting keys of 128 bits in length
*/
int
t_insert6(trie_id_t *tid, key_t id, in6_addr_t key, in6_addr_t mask)
{
node_t *c_node;
int bit, i;
uint8_t pos;
uint8_t type_len = IP_ABITS;
in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
/* don't insert if don't care */
if (IN6_ARE_ADDR_EQUAL(&mask, &zero_addr)) {
++tid->stats.num_dontcare;
return (DONTCARE_VALUE);
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie; /* point at root of trie */
V6_MASK_COPY(key, mask, key); /* apply mask to key */
/*
* A IPv6 address is structured as an array of four uint32_t
* values. The highest order of the bits are located in array[0]
*/
for (i = 0; i < 4; ++i) {
/* traverse trie to the correct position */
for (pos = type_len; pos > 0; --pos) {
/* check if bit is significant */
if (EXTRACTBIT(mask.s6_addr32[i], (pos - 1), type_len)
!= ONE) {
break;
}
bit = EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) {
if (c_node->zero == NULL) {
c_node->zero = create_node(KM_SLEEP);
}
c_node = c_node->zero;
} else { /* ONE bit */
if (c_node->one == NULL) {
c_node->one = create_node(KM_SLEEP);
}
c_node = c_node->one;
}
}
}
/* insert at node */
(void) ipgpc_list_insert(&c_node->elements, id);
/* update stats */
++tid->stats.num_inserted;
/*
* check if this is the first key to be inserted that is not a
* don't care (*)
*/
if (tid->info.dontcareonly == B_TRUE) {
tid->info.dontcareonly = B_FALSE;
}
rw_exit(&tid->rw_lock);
return (NORMAL_VALUE);
}
/*
* t_traverse_delete(in_node, pos, id, key, mask, tid)
*
* used to traverse to the node containing id, as found under key
* once id is found, it is removed from the trie.
* Upon removing the id from a given node in the trie, path compression
* will be applied to nodes that are no longer compressed.
* If the id is successfully removed, tid->stats are updated
*/
static boolean_t
t_traverse_delete(node_t **in_node, uint8_t pos, key_t id, uint32_t key,
uint32_t mask, trie_id_t **tid)
{
node_t *c_node = *in_node;
node_t *t_node;
int bit;
if (c_node == NULL) {
return (B_FALSE); /* base failure case */
}
/* we've found the node the id is probably at */
if ((pos == 0) ||
(EXTRACTBIT(mask, (pos - 1), (uint8_t)(*tid)->key_len) != 1)) {
if (ipgpc_list_remove(&c_node->elements, id) == B_FALSE) {
ipgpc0dbg(("t_traverse_delete: id %d does not " \
"exist in trie\n", id));
return (B_FALSE); /* key does not exist at node */
} else {
/* update stats */
--(*tid)->stats.num_inserted;
/* check if 0 values are inserted in this trie */
if ((*tid)->stats.num_inserted == 0) {
/* update dontcareonly boolean */
(*tid)->info.dontcareonly = B_TRUE;
}
}
/* check if node has zero elements, is a LEAF node */
if ((c_node->elements == NULL) &&
((c_node->one == NULL) && (c_node->zero == NULL))) {
/* make sure we don't delete the root */
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache, c_node);
return (B_TRUE);
} else {
/* this is the root, just zero out the info */
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
/* check to see if node describes bits to skip */
if (c_node->bits > 0) {
if ((key & c_node->mask) != c_node->val) {
ipgpc0dbg(("t_traverse_delete: id %d does not " \
"exist in trie\n", id));
return (B_FALSE); /* key does not exist at node */
}
pos = (c_node->pos - c_node->bits) + 1;
/* search should continue if mask and pos are valid */
if ((pos == 0) ||
(EXTRACTBIT(mask, (pos - 1), (uint8_t)(*tid)->key_len)
!= 1)) {
/* this node probably contains the id */
if (ipgpc_list_remove(&c_node->elements,
id) == B_FALSE) {
ipgpc0dbg(("t_traverse_delete: id %d does" \
"not exist in trie\n", id));
return (B_FALSE);
} else {
/* update stats */
--(*tid)->stats.num_inserted;
/* check if 0 values are inserted */
if ((*tid)->stats.num_inserted == 0) {
/* update dontcare boolean */
(*tid)->info.dontcareonly = B_TRUE;
}
}
/* check if node has zero elements & is a LEAF node */
if ((c_node->elements == NULL) &&
((c_node->one == NULL) &&
(c_node->zero == NULL))) {
/* make sure we don't delete the root */
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache,
c_node);
return (B_TRUE);
} else {
/* this is the root, zero out info */
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
}
/* extract next bit and test */
bit = EXTRACTBIT(key, (pos - 1), (uint8_t)(*tid)->key_len);
if (bit == ZERO) {
if (t_traverse_delete(&c_node->zero, (pos - 1), id, key, mask,
tid) == B_TRUE) {
c_node->zero = NULL;
}
} else { /* ONE bit */
if (t_traverse_delete(&c_node->one, (pos - 1), id, key, mask,
tid) == B_TRUE) {
c_node->one = NULL;
}
}
/*
* non path-compressed nodes will contain one child and no elements
* what occurs here:
* *
* / \
* * * <-- p_node->elements == NULL
* /
* * <-- c_node->elements = foo
* / \
* * * <-- children of c_node
* after:
* *
* / \
* * * <-- p_node->elements = foo
* / \
* * * <-- p_node adopts children of c_node
*/
if ((c_node->one == NULL) && (c_node->zero != NULL)) {
if (c_node->elements == NULL) {
/* move child elements to parent */
c_node->elements = c_node->zero->elements;
/* be sure to include the link in the bits */
c_node->bits += c_node->zero->bits + 1;
/* c_node->pos will remain the same */
c_node->mask |= c_node->zero->mask;
/* don't forget to mark the link */
SETBIT(c_node->mask, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
c_node->val |= c_node->zero->val;
/* don't forget to mark the link */
UNSETBIT(c_node->val, (pos - 1),
(uint8_t)(*tid)->key_len);
/* adopt children */
t_node = c_node->zero;
c_node->one = c_node->zero->one;
c_node->zero = c_node->zero->zero;
kmem_cache_free(trie_node_cache, t_node);
} else {
ASSERT(c_node->zero->one == NULL);
ASSERT(c_node->zero->zero == NULL);
kmem_cache_free(trie_node_cache, c_node->zero);
c_node->zero = NULL;
}
} else if ((c_node->one != NULL) && (c_node->zero == NULL)) {
if (c_node->elements == NULL) {
/* move child elements to parent */
c_node->elements = c_node->one->elements;
/* be sure to include the link in the bits */
c_node->bits += c_node->one->bits + 1;
/* c_node->pos will remain the same */
c_node->mask |= c_node->one->mask;
/* don't forget to mark the link */
SETBIT(c_node->mask, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
c_node->val |= c_node->one->val;
/* don't forget to mark the link */
SETBIT(c_node->val, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
/* adopt children */
t_node = c_node->one;
c_node->zero = c_node->one->zero;
c_node->one = c_node->one->one;
kmem_cache_free(trie_node_cache, t_node);
} else {
ASSERT(c_node->one->one == NULL);
ASSERT(c_node->one->zero == NULL);
kmem_cache_free(trie_node_cache, c_node->one);
c_node->one = NULL;
}
}
/* check if node has zero elements, is a LEAF node */
if ((c_node->elements == NULL) &&
((c_node->one == NULL) && (c_node->zero == NULL))) {
/* make sure we don't delete the root */
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache, c_node);
return (B_TRUE);
} else {
/* this is the root, just zero out the info */
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
/*
* t_remove(tid, id, key, mask)
*
* removes a value associated with an id from the trie
* - if the item does not exist, nothing is removed
* - if more than one id share the same key, only the id specified is removed
*/
void
t_remove(trie_id_t *tid, key_t id, uint32_t key, uint32_t mask)
{
node_t *c_node;
/* don't cares are not inserted */
if (mask == 0) {
--tid->stats.num_dontcare;
return;
}
key &= mask; /* apply mask */
/* traverse to node containing id and remove the id from the trie */
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie;
(void) t_traverse_delete(&c_node, (uint8_t)tid->key_len, id, key, mask,
&tid);
rw_exit(&tid->rw_lock);
}
/*
* t_remove6(tid, id, key, mask)
*
* specific to removing key of 128 bits in length
*/
void
t_remove6(trie_id_t *tid, key_t id, in6_addr_t key, in6_addr_t mask)
{
node_t *c_node;
int bit, i;
uint8_t pos;
uint8_t type_len = IP_ABITS;
in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
/* don't cares are not inserted */
if (IN6_ARE_ADDR_EQUAL(&mask, &zero_addr)) {
--tid->stats.num_dontcare;
return;
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie; /* point at root of trie */
V6_MASK_COPY(key, mask, key);
/*
* A IPv6 address is structured as an array of four uint32_t
* values. The higest order of the bits are located in array[0]
*/
for (i = 0; i < 4; ++i) {
/* traverse trie to the correct position */
for (pos = type_len; pos > 0; --pos) {
/* check if bit is significant */
if (EXTRACTBIT(mask.s6_addr32[i], (pos - 1), type_len)
!= ONE) {
break;
}
bit = EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) {
if (c_node->zero == NULL) {
break;
}
c_node = c_node->zero;
} else { /* ONE bit */
if (c_node->one == NULL) {
break;
}
c_node = c_node->one;
}
}
}
if (c_node != NULL) {
if (ipgpc_list_remove(&c_node->elements, id)) {
/* update stats */
--tid->stats.num_inserted;
/*
* check to see if only dontcare's are inserted
*/
if (tid->stats.num_inserted <= 0) {
tid->info.dontcareonly = B_TRUE;
}
}
}
rw_exit(&tid->rw_lock);
}
/*
* t_retrieve(tid, key, fid_table)
*
* returns the number of found filters that match the input key
* - each value that matches either a partial or exact match on the key
* is inserted into the fid_table
* - some nodes may contain multiple id's, all items will be inserted
* into the fid_table
* - the find stops when an edge node is reached, the left and right nodes
* for the current node are null
* - 0 is returned if no matches are found, otherwise the number of matches
* is returned
* - (-1) is returned if a memory error occurred
*/
int
t_retrieve(trie_id_t *tid, uint32_t key, ht_match_t *fid_table)
{
int bit;
uint8_t pos;
int num_found = 0;
int ret;
node_t *c_node;
rw_enter(&tid->rw_lock, RW_READER);
c_node = tid->trie; /* point at root of trie */
/* ensure trie structure is allocated */
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
/*
* foreach node encountered in the search, collect elements and append
* to a list to be returned
*/
for (pos = (uint8_t)tid->key_len; pos > 0; --pos) {
/* check node for bits to check */
if (c_node->bits > 0) {
if ((key & c_node->mask) != c_node->val) {
rw_exit(&tid->rw_lock);
return (num_found); /* search is done */
}
/* pos is set to next bit not covered by node */
if ((pos = (c_node->pos - c_node->bits) + 1) == 0) {
/* if node covers rest of bits in key */
break;
}
}
/* check node for elements */
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask,
c_node->elements, fid_table)) == -1) {
/* signifies a memory error */
rw_exit(&tid->rw_lock);
return (-1);
}
num_found += ret; /* increment num_found */
}
bit = EXTRACTBIT(key, (pos - 1), (uint8_t)tid->key_len);
if (bit == ZERO) { /* choose leaf */
c_node = c_node->zero;
} else { /* bit == ONE */
c_node = c_node->one;
}
if (c_node == NULL) {
/* search is finished, edge node reached */
rw_exit(&tid->rw_lock);
return (num_found);
}
}
/* see if current node contains elements */
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask, c_node->elements,
fid_table)) == -1) {
rw_exit(&tid->rw_lock);
return (-1); /* signifies a memory error */
}
num_found += ret; /* increment num_found */
}
rw_exit(&tid->rw_lock);
return (num_found);
}
/*
* t_retrieve6(tid, key, fid_table)
*
* specific to retrieving keys of 128 bits in length
*/
int
t_retrieve6(trie_id_t *tid, in6_addr_t key, ht_match_t *fid_table)
{
int bit, i;
uint8_t pos;
int num_found = 0;
int ret;
node_t *c_node;
uint8_t type_len = IP_ABITS;
rw_enter(&tid->rw_lock, RW_READER);
c_node = tid->trie;
/* ensure trie structure is allocated */
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
/*
* A IPv6 address is structured as an array of four uint32_t
* values. The higest order of the bits are located in array[0]
*/
for (i = 0; i < 4; ++i) {
/*
* foreach node encountered in the search, collect elements
* and append to a list to be returned
*/
for (pos = type_len; pos > 0; --pos) {
/* extract bit at pos */
bit =
EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) { /* choose leaf */
c_node = c_node->zero;
} else {
c_node = c_node->one;
}
if (c_node == NULL) {
/* search is finished, edge node reached */
rw_exit(&tid->rw_lock);
return (num_found);
}
/* see if current node contains elements */
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask,
c_node->elements, fid_table)) == -1) {
/* signifies a memory error */
rw_exit(&tid->rw_lock);
return (-1);
}
num_found += ret; /* increment num_found */
}
}
}
rw_exit(&tid->rw_lock);
return (num_found);
}