// -*- c-basic-offset: 4 -*-
/*
* processingt.{cc,hh} -- decide on a Click configuration's processing
* Eddie Kohler
*
* Copyright (c) 2000 Massachusetts Institute of Technology
* Copyright (c) 2001 International Computer Science Institute
*
* 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 "processingt.hh"
#include <click/error.hh>
#include <click/bitvector.hh>
#include <click/straccum.hh>
#include "elementmap.hh"
#include <string.h>
const char ProcessingT::processing_letters[] = "ahl";
ProcessingT::ProcessingT()
: _router(0)
{
}
ProcessingT::ProcessingT(const RouterT *r, ErrorHandler *errh)
: _router(0)
{
reset(r, ElementMap::default_map(), false, errh);
}
ProcessingT::ProcessingT(const RouterT *r, ElementMap *em, ErrorHandler *errh)
: _router(0)
{
reset(r, em, false, errh);
}
ProcessingT::ProcessingT(const RouterT *r, ElementMap *em, bool flatten,
ErrorHandler *errh)
: _router(0)
{
reset(r, em, flatten, errh);
}
void
ProcessingT::create_pidx(ErrorHandler *errh)
{
int ne = _router->nelements();
_input_pidx.assign(ne, 0);
_output_pidx.assign(ne, 0);
// count used input and output ports for each element
int ci = 0, co = 0;
for (int i = 0; i < ne; i++) {
_input_pidx[i] = ci;
_output_pidx[i] = co;
ci += _router->element(i)->ninputs();
co += _router->element(i)->noutputs();
}
_input_pidx.push_back(ci);
_output_pidx.push_back(co);
// create eidxes
_input_elt.clear();
_output_elt.clear();
ci = 0, co = 0;
for (int i = 1; i <= ne; i++) {
const ElementT *e = _router->element(i - 1);
for (; ci < _input_pidx[i]; ci++)
_input_elt.push_back(e);
for (; co < _output_pidx[i]; co++)
_output_elt.push_back(e);
}
// complain about dead elements with live connections
if (errh) {
for (RouterT::const_iterator x = _router->begin_elements(); x; x++)
if (x->dead() && (x->ninputs() > 0 || x->noutputs() > 0))
errh->lwarning(x->landmark(), "dead element %s has live connections", x->name_c_str());
}
}
static int
next_processing_code(const String &str, int &pos, ErrorHandler *errh,
const String &landmark, ElementClassT *etype)
{
const char *s = str.data();
int len = str.length();
if (pos >= len)
return -2;
switch (s[pos]) {
case 'h': case 'H':
pos++;
return ProcessingT::VPUSH;
case 'l': case 'L':
pos++;
return ProcessingT::VPULL;
case 'a': case 'A':
pos++;
return ProcessingT::VAGNOSTIC;
case '/':
return -2;
default:
errh->lerror(landmark, "bad character '%c' in processing code for '%s'", s[pos], etype->printable_name_c_str());
pos++;
return -1;
}
}
void
ProcessingT::initial_processing_for(int ei, ErrorHandler *errh)
{
// don't handle uprefs or tunnels
const ElementT *e = _router->element(ei);
ElementClassT *etype = e->type();
if (!etype || etype == ElementClassT::tunnel_type())
return;
String landmark = e->landmark();
String pc = etype->traits().processing_code;
if (!pc) {
errh->lwarning(landmark, "'%s' has no processing code; assuming agnostic", etype->printable_name_c_str());
return;
}
int pos = 0;
int len = pc.length();
int start_in = _input_pidx[ei];
int start_out = _output_pidx[ei];
int val = 0;
int last_val = 0;
for (int i = 0; i < e->ninputs(); i++) {
if (last_val >= 0)
last_val = next_processing_code(pc, pos, errh, landmark, etype);
if (last_val >= 0)
val = last_val;
_input_processing[start_in + i] = val;
}
while (pos < len && pc[pos] != '/')
pos++;
if (pos >= len)
pos = 0;
else
pos++;
last_val = 0;
for (int i = 0; i < e->noutputs(); i++) {
if (last_val >= 0)
last_val = next_processing_code(pc, pos, errh, landmark, etype);
if (last_val >= 0)
val = last_val;
_output_processing[start_out + i] = val;
}
}
void
ProcessingT::initial_processing(ErrorHandler *errh)
{
_input_processing.assign(ninput_pidx(), VAGNOSTIC);
_output_processing.assign(noutput_pidx(), VAGNOSTIC);
for (int i = 0; i < nelements(); i++)
initial_processing_for(i, errh);
}
void
ProcessingT::processing_error(const ConnectionT &conn, int processing_from,
ErrorHandler *errh)
{
const char *type1 = (processing_from == VPUSH ? "push" : "pull");
const char *type2 = (processing_from == VPUSH ? "pull" : "push");
if (conn.landmark() == "<agnostic>")
errh->lerror(conn.from_element()->landmark(),
"agnostic '%s' in mixed context: %s input %d, %s output %d",
conn.from_element()->name_c_str(), type2, conn.to_port(),
type1, conn.from_port());
else
errh->lerror(conn.landmark(),
"'%s' %s output %d connected to '%s' %s input %d",
conn.from_element()->name_c_str(), type1, conn.from_port(),
conn.to_element()->name_c_str(), type2, conn.to_port());
}
void
ProcessingT::check_processing(ErrorHandler *errh)
{
// add fake connections for agnostics
Vector<ConnectionT> conn = _router->connections();
Bitvector bv;
for (int i = 0; i < ninput_pidx(); i++)
if (_input_processing[i] == VAGNOSTIC) {
ElementT *e = const_cast<ElementT *>(_input_elt[i]);
int ei = e->eindex();
int port = i - _input_pidx[ei];
int opidx = _output_pidx[ei];
int noutputs = _output_pidx[ei+1] - opidx;
forward_flow(e->type()->traits().flow_code,
port, noutputs, &bv, errh);
for (int j = 0; j < noutputs; j++)
if (bv[j] && _output_processing[opidx + j] == VAGNOSTIC)
conn.push_back(ConnectionT(PortT(e, j), PortT(e, port), "<agnostic>"));
}
// spread personalities
while (true) {
bool changed = false;
for (int c = 0; c < conn.size(); c++) {
if (conn[c].dead())
continue;
int offf = output_pidx(conn[c].from());
int offt = input_pidx(conn[c].to());
int pf = _output_processing[offf];
int pt = _input_processing[offt];
switch (pt) {
case VAGNOSTIC:
if (pf != VAGNOSTIC) {
_input_processing[offt] = pf;
changed = true;
}
break;
case VPUSH:
case VPULL:
if (pf == VAGNOSTIC) {
_output_processing[offf] = pt;
changed = true;
} else if (pf != pt) {
processing_error(conn[c], pf, errh);
conn[c].kill();
}
break;
}
}
if (!changed)
break;
}
}
static const char *
processing_name(int p)
{
if (p == ProcessingT::VAGNOSTIC)
return "agnostic";
else if (p == ProcessingT::VPUSH)
return "push";
else if (p == ProcessingT::VPULL)
return "pull";
else
return "?";
}
void
ProcessingT::check_connections(ErrorHandler *errh)
{
Vector<int> input_used(ninput_pidx(), -1);
Vector<int> output_used(noutput_pidx(), -1);
// Check each hookup to ensure it doesn't reuse a port
const Vector<ConnectionT> &conn = _router->connections();
for (int c = 0; c < conn.size(); c++) {
if (conn[c].dead())
continue;
const PortT &hf = conn[c].from(), &ht = conn[c].to();
int fp = output_pidx(hf), tp = input_pidx(ht);
if (_output_processing[fp] == VPUSH && output_used[fp] >= 0) {
errh->lerror(conn[c].landmark(),
"reuse of '%s' push output %d",
hf.element->name_c_str(), hf.port);
errh->lmessage(conn[output_used[fp]].landmark(),
" '%s' output %d previously used here",
hf.element->name_c_str(), hf.port);
} else
output_used[fp] = c;
if (_input_processing[tp] == VPULL && input_used[tp] >= 0) {
errh->lerror(conn[c].landmark(),
"reuse of '%s' pull input %d",
ht.element->name_c_str(), ht.port);
errh->lmessage(conn[input_used[tp]].landmark(),
" '%s' input %d previously used here",
ht.element->name_c_str(), ht.port);
} else
input_used[tp] = c;
}
// Check for unused inputs and outputs, set _connected_* properly.
for (int i = 0; i < ninput_pidx(); i++)
if (input_used[i] < 0) {
const ElementT *e = _input_elt[i];
if (e->dead())
continue;
int port = i - _input_pidx[e->eindex()];
errh->lerror(e->landmark(),
"'%s' %s input %d not connected",
e->name_c_str(), processing_name(_input_processing[i]), port);
}
for (int i = 0; i < noutput_pidx(); i++)
if (output_used[i] < 0) {
const ElementT *e = _output_elt[i];
if (e->dead())
continue;
int port = i - _output_pidx[e->eindex()];
errh->lerror(e->landmark(),
"'%s' %s output %d not connected",
e->name_c_str(), processing_name(_output_processing[i]), port);
}
// Set _connected_* properly.
PortT crap(0, -1);
_connected_input.assign(ninput_pidx(), crap);
_connected_output.assign(noutput_pidx(), crap);
for (int i = 0; i < ninput_pidx(); i++)
if (_input_processing[i] == VPULL && input_used[i] >= 0)
_connected_input[i] = conn[ input_used[i] ].from();
for (int i = 0; i < noutput_pidx(); i++)
if (_output_processing[i] == VPUSH && output_used[i] >= 0)
_connected_output[i] = conn[ output_used[i] ].to();
}
int
ProcessingT::reset(const RouterT *r, ElementMap *emap, bool flatten,
ErrorHandler *errh)
{
_router = r;
if (!errh)
errh = ErrorHandler::silent_handler();
int before = errh->nerrors();
ElementMap::push_default(emap);
// create pidx and elt arrays, warn about dead elements
create_pidx(errh);
initial_processing(errh);
check_processing(errh);
// 'flat' configurations have no agnostic ports; change them to push
if (flatten)
resolve_agnostics();
check_connections(errh);
ElementMap::pop_default();
if (errh->nerrors() != before)
return -1;
return 0;
}
void
ProcessingT::resolve_agnostics()
{
for (int i = 0; i < _input_processing.size(); i++)
if (_input_processing[i] == VAGNOSTIC)
_input_processing[i] = VPUSH;
for (int i = 0; i < _output_processing.size(); i++)
if (_output_processing[i] == VAGNOSTIC)
_output_processing[i] = VPUSH;
}
bool
ProcessingT::same_processing(int a, int b) const
{
int ai = _input_pidx[a], bi = _input_pidx[b];
int ao = _output_pidx[a], bo = _output_pidx[b];
int ani = _input_pidx[a+1] - ai, bni = _input_pidx[b+1] - bi;
int ano = _output_pidx[a+1] - ao, bno = _output_pidx[b+1] - bo;
if (ani != bni || ano != bno)
return false;
if (ani && memcmp(&_input_processing[ai], &_input_processing[bi], sizeof(int) * ani) != 0)
return false;
if (ano && memcmp(&_output_processing[ao], &_output_processing[bo], sizeof(int) * ano) != 0)
return false;
return true;
}
String
ProcessingT::processing_code(const ElementT *e) const
{
assert(e->router() == _router);
int ei = e->eindex();
StringAccum sa;
for (int i = _input_pidx[ei]; i < _input_pidx[ei+1]; i++)
sa << processing_letters[_input_processing[i]];
sa << '/';
for (int i = _output_pidx[ei]; i < _output_pidx[ei+1]; i++)
sa << processing_letters[_output_processing[i]];
return sa.take_string();
}
// FLOW CODES
static void
skip_flow_code(const char *&p, const char *last)
{
if (p != last && *p != '/') {
if (*p == '[') {
for (p++; p != last && *p != ']'; p++)
/* nada */;
if (p != last)
p++;
} else
p++;
}
}
static int
next_flow_code(const char *&p, const char *last,
int port, Bitvector &code, ErrorHandler *errh)
{
if (p == last || *p == '/') {
// back up to last code character
if (p[-1] == ']') {
for (p -= 2; *p != '['; p--)
/* nada */;
} else
p--;
}
code.assign(256, false);
if (*p == '[') {
bool negated = false;
if (p[1] == '^')
negated = true, p++;
for (p++; p != last && *p != ']'; p++) {
// avoid isalpha() to avoid locale/"signed char" dependencies
if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))
code[*p] = true;
else if (*p == '#')
code[port + 128] = true;
else if (errh)
errh->error("flow code: invalid character '%c'", *p);
}
if (negated)
code.negate();
if (p == last) {
if (errh)
errh->error("flow code: missing ']'");
p--; // don't skip over final '\0'
}
} else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))
code[*p] = true;
else if (*p == '#')
code[port + 128] = true;
else {
if (errh)
errh->error("flow code: invalid character '%c'", *p);
p++;
return -1;
}
p++;
return 0;
}
int
ProcessingT::forward_flow(const String &flow_code, int input_port,
int noutputs, Bitvector *bv, ErrorHandler *errh)
{
if (input_port < 0) {
bv->assign(noutputs, false);
return 0;
} else if (!flow_code || (flow_code.length() == 3 && flow_code == "x/x")) {
bv->assign(noutputs, true);
return 0;
}
bv->assign(noutputs, false);
const char *f_in = flow_code.begin();
const char *f_out = find(flow_code, '/');
const char *f_last = flow_code.end();
f_out = (f_out == f_last ? f_in : f_out + 1);
if (f_out == f_last || *f_out == '/')
return (errh ? errh->error("flow code: missing or bad '/'") : -1);
Bitvector in_code;
for (int i = 0; i < input_port; i++)
skip_flow_code(f_in, f_last);
next_flow_code(f_in, f_last, input_port, in_code, errh);
Bitvector out_code;
for (int i = 0; i < noutputs; i++) {
next_flow_code(f_out, f_last, i, out_code, errh);
if (in_code.nonzero_intersection(out_code))
(*bv)[i] = true;
}
return 0;
}
int
ProcessingT::backward_flow(const String &flow_code, int output_port,
int ninputs, Bitvector *bv, ErrorHandler *errh)
{
if (output_port < 0) {
bv->assign(ninputs, false);
return 0;
} else if (!flow_code || (flow_code.length() == 3 && flow_code == "x/x")) {
bv->assign(ninputs, true);
return 0;
}
bv->assign(ninputs, false);
const char *f_in = flow_code.begin();
const char *f_out = find(flow_code, '/');
const char *f_last = flow_code.end();
f_out = (f_out == f_last ? f_in : f_out + 1);
if (f_out == f_last || *f_out == '/')
return (errh ? errh->error("flow code: missing or bad '/'") : -1);
Bitvector out_code;
for (int i = 0; i < output_port; i++)
skip_flow_code(f_out, f_last);
next_flow_code(f_out, f_last, output_port, out_code, errh);
Bitvector in_code;
for (int i = 0; i < ninputs; i++) {
next_flow_code(f_in, f_last, i, in_code, errh);
if (in_code.nonzero_intersection(out_code))
(*bv)[i] = true;
}
return 0;
}
void
ProcessingT::set_connected_inputs(const Bitvector &outputs, Bitvector &inputs) const
{
assert(outputs.size() == noutput_pidx() && inputs.size() == ninput_pidx());
const Vector<ConnectionT> &conn = _router->connections();
for (int i = 0; i < conn.size(); i++)
if (outputs[output_pidx(conn[i])])
inputs[input_pidx(conn[i])] = true;
}
void
ProcessingT::set_connected_outputs(const Bitvector &inputs, Bitvector &outputs) const
{
assert(outputs.size() == noutput_pidx() && inputs.size() == ninput_pidx());
const Vector<ConnectionT> &conn = _router->connections();
for (int i = 0; i < conn.size(); i++)
if (inputs[input_pidx(conn[i])])
outputs[output_pidx(conn[i])] = true;
}
void
ProcessingT::set_connected_inputs(const PortT &port, Bitvector &inputs) const
{
assert(port.router() == _router && inputs.size() == ninput_pidx());
const Vector<ConnectionT> &conn = _router->connections();
for (int i = 0; i < conn.size(); i++)
if (conn[i].from() == port)
inputs[input_pidx(conn[i])] = true;
}
void
ProcessingT::set_connected_outputs(const PortT &port, Bitvector &outputs) const
{
assert(port.router() == _router && outputs.size() == noutput_pidx());
const Vector<ConnectionT> &conn = _router->connections();
for (int i = 0; i < conn.size(); i++)
if (conn[i].to() == port)
outputs[output_pidx(conn[i])] = true;
}
void
ProcessingT::set_flowed_inputs(const Bitvector &outputs, Bitvector &inputs, ErrorHandler *errh) const
{
assert(outputs.size() == noutput_pidx() && inputs.size() == ninput_pidx());
Bitvector bv;
// for speed with sparse Bitvectors, look into the Bitvector implementation
const uint32_t *output_udata = outputs.data_words();
for (int i = 0; i <= outputs.max_word(); i++)
if (output_udata[i]) {
int m = (i*8 + 8 > outputs.size() ? outputs.size() : i*8 + 8);
for (int j = i*8; j < m; j++)
if (outputs[j]) {
PortT p = output_port(j);
(void) backward_flow(p, &bv, errh);
inputs.or_at(bv, _input_pidx[p.element->eindex()]);
}
}
}
void
ProcessingT::set_flowed_outputs(const Bitvector &inputs, Bitvector &outputs, ErrorHandler *errh) const
{
assert(outputs.size() == noutput_pidx() && inputs.size() == ninput_pidx());
Bitvector bv;
// for speed with sparse Bitvectors, look into the Bitvector implementation
const uint32_t *input_udata = inputs.data_words();
for (int i = 0; i <= inputs.max_word(); i++)
if (input_udata[i]) {
int m = (i*8 + 8 > inputs.size() ? inputs.size() : i*8 + 8);
for (int j = i*8; j < m; j++)
if (inputs[j]) {
PortT p = input_port(j);
(void) forward_flow(p, &bv, errh);
outputs.or_at(bv, _output_pidx[p.element->eindex()]);
}
}
}
void
ProcessingT::forward_reachable_inputs(Bitvector &inputs, ErrorHandler *errh) const
{
assert(inputs.size() == ninput_pidx());
Bitvector outputs(noutput_pidx(), false);
Bitvector new_inputs(ninput_pidx(), false), diff(inputs);
while (1) {
set_flowed_outputs(diff, outputs, errh);
set_connected_inputs(outputs, new_inputs);
inputs.or_with_difference(new_inputs, diff);
if (!diff)
return;
}
}
String
ProcessingT::compound_processing_code() const
{
ElementT *input = const_cast<ElementT *>(_router->element("input"));
ElementT *output = const_cast<ElementT *>(_router->element("output"));
assert(input && output && input->tunnel() && output->tunnel());
// read input and output codes
StringAccum icode, ocode;
for (int i = 0; i < input->noutputs(); i++) {
int p = output_processing(PortT(input, i));
icode << processing_letters[p];
}
for (int i = 0; i < output->ninputs(); i++) {
int p = input_processing(PortT(output, i));
ocode << processing_letters[p];
}
// streamline codes, ensure at least one character per half
while (icode.length() > 1 && icode[icode.length() - 2] == icode.back())
icode.pop_back();
while (ocode.length() > 1 && ocode[ocode.length() - 2] == ocode.back())
ocode.pop_back();
if (!icode.length())
icode << 'a';
if (!ocode.length())
ocode << 'a';
icode << '/' << ocode;
return icode.take_string();
}
String
ProcessingT::compound_flow_code(ErrorHandler *errh) const
{
ElementT *input = const_cast<ElementT *>(_router->element("input"));
ElementT *output = const_cast<ElementT *>(_router->element("output"));
assert(input && output && input->tunnel() && output->tunnel());
// skip calculation in common case
int ninputs = input->noutputs(), noutputs = output->ninputs();
if (ninputs == 0 || noutputs == 0)
return "x/y";
// read flow codes, create 'codes' array
Bitvector *codes = new Bitvector[noutputs];
for (int i = 0; i < noutputs; i++)
codes[i].assign(ninputs, false);
Bitvector input_vec(ninput_pidx(), false);
int opidx = input_pidx(PortT(output, 0));
for (int i = 0; i < ninputs; i++) {
if (i)
input_vec.clear();
set_connected_inputs(PortT(input, i), input_vec);
forward_reachable_inputs(input_vec, errh);
for (int p = 0; p < noutputs; p++)
if (input_vec[opidx + p])
codes[p][i] = true;
}
// combine flow codes
Vector<int> codeid;
const char *cur_code = "xyzabcdefghijklmnopqrstuvwXYZABCDEFGHIJKLMNOPQRSTUVW0123456789_";
codeid.push_back(*cur_code++);
for (int i = 1; i < ninputs; i++) {
// look for flow codes common among all outputs with this code, and
// flow codes present in any output without this code
Bitvector common(ninputs, true);
Bitvector disjoint(ninputs, false);
int found = 0;
for (int j = 0; j < noutputs; j++)
if (codes[j][i]) {
common &= codes[j];
found++;
} else
disjoint |= codes[j];
disjoint.negate();
common &= disjoint;
for (int j = 0; j < i; j++)
if (common[j]) {
codeid.push_back(codeid[j]);
// turn off reference
for (int k = 0; k < noutputs; k++)
codes[k][i] = false;
goto found;
}
assert(*cur_code);
codeid.push_back(*cur_code++);
found: ;
}
// generate flow code
assert(*cur_code);
StringAccum sa;
for (int i = 0; i < ninputs; i++)
sa << (char)codeid[i];
sa << '/';
for (int i = 0; i < noutputs; i++)
if (!codes[i])
sa << *cur_code;
else {
int pos = sa.length();
sa << '[';
for (int j = 0; j < ninputs; j++)
if (codes[i][j])
sa << (char)codeid[j];
if (sa.length() == pos + 2) {
sa[pos] = sa[pos + 1];
sa.pop_back();
} else
sa << ']';
}
// return
delete[] codes;
return sa.take_string();
}
syntax highlighted by Code2HTML, v. 0.9.1