// -*- c-basic-offset: 4; related-file-name: "../include/click/nameinfo.hh" -*-
/*
* nameinfo.{cc,hh} -- stores name information
* Eddie Kohler
*
* Copyright (c) 2005 The Regents of the University of California
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <click/nameinfo.hh>
#include <click/glue.hh>
#include <click/confparse.hh>
#include <click/router.hh>
#include <click/error.hh>
CLICK_DECLS
static NameInfo *the_name_info;
void
NameDB::define(const String &, const void *, int)
{
}
String
NameDB::revfind(const void *, int)
{
return String();
}
bool
StaticNameDB::query(const String &name, void *value, int vsize)
{
assert(vsize == 4);
const char *namestr = name.c_str();
int l = 0, r = _nentries - 1;
while (l <= r) {
int m = (l + r) / 2;
int cmp = strcmp(namestr, _entries[m].name);
if (cmp == 0) {
*reinterpret_cast<uint32_t *>(value) = _entries[m].value;
return true;
} else if (cmp < 0)
r = m - 1;
else
l = m + 1;
}
return false;
}
String
StaticNameDB::revfind(const void *value, int vsize)
{
assert(vsize == 4);
uint32_t ivalue;
memcpy(&ivalue, value, 4);
for (int i = 0; i < _nentries; i++)
if (_entries[i].value == ivalue)
return String::stable_string(_entries[i].name);
return String();
}
void *
DynamicNameDB::find(const String &name, bool create)
{
if (_sorted > 20)
sort();
if (_sorted == 100) {
int l = 0, r = _names.size() - 1, m;
while (l <= r) {
m = (l + r) / 2;
int cmp = String::compare(name, _names[m]);
if (cmp == 0)
return _values.data() + value_size() * m;
else if (cmp < 0)
r = m - 1;
else
l = m + 1;
}
} else {
_sorted++;
for (int i = 0; i < _names.size(); i++)
if (name == _names[i])
return _values.data() + value_size() * i;
}
if (create) {
_sorted = 0;
_names.push_back(name);
_values.extend(value_size());
return _values.data() + _values.length() - value_size();
} else
return 0;
}
static int
namelist_sort_compar(const void *athunk, const void *bthunk, void *othunk)
{
const int *a = (const int *) athunk, *b = (const int *) bthunk;
const String *o = (const String *) othunk;
return String::compare(o[*a], o[*b]);
}
void
DynamicNameDB::sort()
{
if (_sorted == 100)
return;
Vector<int> permutation(_names.size(), 0);
for (int i = 0; i < _names.size(); i++)
permutation[i] = i;
click_qsort(permutation.begin(), permutation.size(), sizeof(int), namelist_sort_compar, _names.begin());
Vector<String> new_names(_names.size(), String());
StringAccum new_values(_values.length());
new_values.extend(_values.length());
String *nn = new_names.begin();
char *nv = new_values.data();
for (int i = 0; i < _names.size(); i++, nn++, nv += value_size()) {
*nn = _names[permutation[i]];
memcpy(nv, _values.data() + value_size() * permutation[i], value_size());
}
_names.swap(new_names);
_values.swap(new_values);
_sorted = 100;
}
bool
DynamicNameDB::query(const String& name, void* value, int vsize)
{
assert(value_size() == vsize);
if (void *x = find(name, false)) {
memcpy(value, x, vsize);
return true;
} else
return false;
}
void
DynamicNameDB::define(const String& name, const void* value, int vsize)
{
assert(value_size() == vsize);
if (void *x = find(name, true))
memcpy(x, value, vsize);
}
String
DynamicNameDB::revfind(const void *value, int vsize)
{
const uint8_t *dx = (const uint8_t *) _values.data();
for (int i = 0; i < _names.size(); i++, dx += vsize)
if (memcmp(dx, value, vsize) == 0)
return _names[i];
return String();
}
NameInfo::NameInfo()
{
#ifdef CLICK_NAMEDB_CHECK
_check_generation = (uintptr_t) this;
#endif
}
NameInfo::~NameInfo()
{
for (int i = 0; i < _namedbs.size(); i++)
delete _namedbs[i];
}
void
NameInfo::static_initialize()
{
the_name_info = new NameInfo;
}
void
NameInfo::static_cleanup()
{
delete the_name_info;
}
#if 0
String
NameInfo::NameList::rlookup(uint32_t val)
{
assert(_value_size == 4);
const uint32_t *x = (const uint32_t *) _values.value_size();
for (int i = 0; i < _names.size(); i++)
if (x[i] == val)
return _names[i];
return String();
}
#endif
NameDB *
NameInfo::namedb(uint32_t type, int vsize, const String &prefix, NameDB *install)
{
NameDB *db;
// binary-search types
int l = 0, r = _namedb_roots.size() - 1, m;
while (l <= r) {
m = (l + r) / 2;
if (type == _namedb_roots[m]->_type)
goto found_root;
else if (type < _namedb_roots[m]->_type)
r = m - 1;
else
l = m + 1;
}
// type not found
if (install == install_dynamic_sentinel())
install = new DynamicNameDB(type, prefix, vsize);
if (install) {
assert(!install->_installed);
install->_installed = this;
_namedbs.push_back(install);
_namedb_roots.insert(_namedb_roots.begin() + l, install);
return install;
} else
return 0;
found_root:
// walk tree to find prefix match; keep track of closest prefix
db = _namedb_roots[m];
NameDB *closest = 0;
while (db) {
if (db->_prefix.length() <= prefix.length()
&& memcmp(db->_prefix.data(), prefix.data(), db->_prefix.length()) == 0) {
closest = db;
db = db->_prefix_child;
} else
db = db->_prefix_sibling;
}
// prefix found?
if (closest && closest->_prefix == prefix) {
assert(vsize < 0 || closest->_value_size == vsize);
return closest;
}
// prefix not found
if (install == install_dynamic_sentinel())
install = new DynamicNameDB(type, prefix, vsize);
if (install) {
assert(!install->_installed);
install->_installed = this;
_namedbs.push_back(install);
install->_prefix_parent = closest;
NameDB **pp = (closest ? &closest->_prefix_child : &_namedb_roots[m]);
install->_prefix_sibling = *pp;
*pp = install;
// adopt nodes that should be our children
pp = &install->_prefix_sibling;
while (*pp) {
if (prefix.length() < (*pp)->_prefix.length()
&& memcmp((*pp)->_prefix.data(), prefix.data(), prefix.length()) == 0) {
NameDB *new_child = *pp;
*pp = new_child->_prefix_sibling;
new_child->_prefix_parent = install;
new_child->_prefix_sibling = install->_prefix_child;
install->_prefix_child = new_child;
} else
pp = &(*pp)->_prefix_sibling;
}
return install;
} else {
assert(!closest || vsize < 0 || closest->_value_size == vsize);
return closest;
}
}
NameDB *
NameInfo::getdb(uint32_t type, const Element *e, int vsize, bool create)
{
if (e) {
if (NameInfo *ni = (create ? e->router()->force_name_info() : e->router()->name_info())) {
NameDB *install = (create ? ni->install_dynamic_sentinel() : 0);
String ename = e->name();
int last_slash = ename.find_right('/');
if (last_slash >= 0)
return ni->namedb(type, vsize, ename.substring(0, last_slash + 1), install);
else
return ni->namedb(type, vsize, String(), install);
}
}
NameDB *install = (create ? the_name_info->install_dynamic_sentinel() : 0);
return the_name_info->namedb(type, vsize, String(), install);
}
void
NameInfo::installdb(NameDB *db, const Element *prefix)
{
NameInfo *ni = (prefix ? prefix->router()->force_name_info() : the_name_info);
NameDB *curdb = ni->namedb(db->type(), db->value_size(), db->prefix(), db);
if (curdb != db) {
assert(!curdb->_prefix_child || curdb->_prefix_child->prefix().length() > db->prefix().length());
db->_installed = ni;
db->_prefix_child = curdb->_prefix_child;
db->_prefix_parent = curdb;
curdb->_prefix_child = db;
for (NameDB *child = db->_prefix_child; child; child = child->_prefix_sibling)
child->_prefix_parent = db;
ni->_namedbs.push_back(db);
}
#if CLICK_NAMEDB_CHECK
ni->check(ErrorHandler::default_handler());
#endif
}
void
NameInfo::removedb(NameDB *db)
{
if (!db->_installed)
return;
// This is an uncommon operation, so don't worry about its performance.
NameInfo *ni = db->_installed;
int m;
for (m = 0; m < ni->_namedb_roots.size(); m++)
if (ni->_namedb_roots[m]->_type == db->_type)
break;
NameDB **pp = (db->_prefix_parent ? &db->_prefix_parent->_prefix_child
: &ni->_namedb_roots[m]);
// Remove from sibling list
for (NameDB *sib = *pp; sib != db; sib = sib->_prefix_sibling)
/* do nothing */;
// Patch children in
*pp = db->_prefix_sibling;
while (NameDB *cdb = db->_prefix_child) {
db->_prefix_child = cdb->_prefix_sibling;
cdb->_prefix_parent = db->_prefix_parent;
cdb->_prefix_sibling = *pp;
*pp = cdb;
}
// Maybe remove root
if (!*pp && !db->_prefix_parent)
ni->_namedb_roots.erase(pp);
// Remove from _namedbs
for (int i = 0; i < ni->_namedbs.size(); i++)
if (ni->_namedbs[i] == db) {
ni->_namedbs[i] = ni->_namedbs.back();
ni->_namedbs.pop_back();
break;
}
// Mark as not installed
db->_installed = 0;
#if CLICK_NAMEDB_CHECK
ni->check(ErrorHandler::default_handler());
#endif
}
bool
NameInfo::query(uint32_t type, const Element *e, const String &name, void *value, int vsize)
{
while (1) {
NameDB *db = getdb(type, e, vsize, false);
while (db) {
if (db->query(name, value, vsize))
return true;
db = db->prefix_parent();
}
if (!e)
return false;
e = 0;
}
}
bool
NameInfo::query_int(uint32_t type, const Element *e, const String &name, int32_t *value)
{
return query(type, e, name, value, 4) || cp_integer(name, value);
}
bool
NameInfo::query_int(uint32_t type, const Element *e, const String &name, uint32_t *value)
{
return query(type, e, name, value, 4) || cp_unsigned(name, value);
}
String
NameInfo::revquery(uint32_t type, const Element *e, const void *value, int vsize)
{
while (1) {
NameDB *db = getdb(type, e, vsize, false);
while (db) {
if (String s = db->revfind(value, vsize))
return s;
db = db->prefix_parent();
}
if (!e)
return String();
e = 0;
}
}
#ifdef CLICK_NAMEDB_CHECK
void
NameInfo::check(ErrorHandler *errh)
{
StringAccum sa;
sa << "NameInfo[" << (void*) this << "]: ";
PrefixErrorHandler perrh(errh, sa.take_string());
_check_generation++;
for (int i = 0; i < _namedb_roots.size(); i++) {
NameDB *db = _namedb_roots[i];
if (i < _namedb_roots.size() - 1
&& db->type() >= _namedb_roots[i+1]->type())
perrh.error("db roots out of order at %i (%x/%x)", i, (unsigned) db->type(), (unsigned) _namedb_roots[i+1]->type());
checkdb(db, 0, &perrh);
}
for (int i = 0; i < _namedbs.size(); i++)
if (_namedbs[i]->_check_generation != _check_generation)
perrh.error("DB[%x %s %p] in namedbs, but inaccessible", _namedbs[i]->_type, _namedbs[i]->_prefix.c_str(), _namedbs[i]);
}
void
NameInfo::checkdb(NameDB *db, NameDB *parent, ErrorHandler *errh)
{
StringAccum sa;
sa.snprintf(20, "DB[%x ", db->_type);
if (db->_prefix)
sa << db->_prefix << ' ';
sa << (void*) db << "]: ";
PrefixErrorHandler perrh(errh, sa.take_string());
// check self
if (!db->_installed)
perrh.error("not installed");
else if (db->_installed != this)
perrh.error("installed in %p, not this NameInfo", db->_installed);
if (db->_check_generation == _check_generation)
perrh.error("installed in more than one place");
db->_check_generation = _check_generation;
for (int i = 0; i < _namedbs.size(); i++)
if (_namedbs[i] == db)
goto found_in_namedbs;
perrh.error("not in _namedbs");
found_in_namedbs:
// check parent relationships
if (db->_prefix_parent != parent)
perrh.error("bad parent (%p/%p)", db->_prefix_parent, parent);
else if (parent && (db->_prefix.length() < parent->_prefix.length()
|| db->_prefix.substring(0, parent->_prefix.length()) != parent->_prefix))
perrh.error("parent prefix (%s) disagrees with prefix", parent->_prefix.c_str());
if (db->_prefix && db->_prefix.back() != '/')
perrh.error("prefix doesn't end with '/'");
if (parent && parent->_type != db->_type)
perrh.error("parent DB[%x %s %p] has different type", parent->_type, parent->_prefix.c_str(), parent);
if (parent && parent->_value_size != db->_value_size)
perrh.error("parent DB[%x %s %p] has different value size (%u/%u)", parent->_type, parent->_prefix.c_str(), parent, parent->_value_size, db->_value_size);
// check sibling relationships
for (NameDB* sib = db->_prefix_sibling; sib; sib = sib->_prefix_sibling) {
int l1 = db->_prefix.length(), l2 = sib->_prefix.length();
if (l1 < l2 ? sib->_prefix.substring(0, l1) == db->_prefix
: db->_prefix.substring(0, l2) == sib->_prefix)
perrh.error("sibling DB[%x %s %p] should have parent/child relationship", sib->_type, sib->_prefix.c_str(), sib);
if (sib->_type != db->_type)
perrh.error("sibling DB[%x %s %p] has different type", sib->_type, sib->_prefix.c_str(), sib);
if (sib->_value_size != db->_value_size)
perrh.error("sibling DB[%x %s %p] has different value size (%u/%u)", sib->_type, sib->_prefix.c_str(), sib, sib->_value_size, db->_value_size);
}
// check db itself
db->check(&perrh);
// recurse down and to the side
perrh.message("OK");
if (db->_prefix_child) {
PrefixErrorHandler perrh2(errh, " ");
checkdb(db->_prefix_child, db, &perrh2);
}
if (db->_prefix_sibling)
checkdb(db->_prefix_sibling, parent, errh);
}
void
NameDB::check(ErrorHandler *)
{
}
void
StaticNameDB::check(ErrorHandler *errh)
{
for (int i = 0; i < _nentries - 1; i++)
if (strcmp(_entries[i].name, _entries[i+1].name) >= 0)
errh->error("entries %d/%d (%s/%s) out of order", i, i+1, _entries[i].name, _entries[i+1].name);
}
void
DynamicNameDB::check(ErrorHandler *errh)
{
if (_sorted == 100)
for (int i = 0; i < _names.size() - 1; i++)
if (String::compare(_names[i], _names[i+1]) >=0)
errh->error("entries %d/%d (%s/%s) out of order", i, i+1, _names[i].c_str(), _names[i+1].c_str());
if (_values.length() != _names.size() * value_size())
errh->error("odd value length %d (should be %d)", _values.length(), _names.size() * value_size());
}
void
NameInfo::check(const Element *e, ErrorHandler *errh)
{
if (e)
if (NameInfo *ni = e->router()->name_info())
ni->check(errh);
the_name_info->check(errh);
}
#endif
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1