TopCoder challenge: SimpleRouter

This implementation is by no means elegant: it does not handle error well; it uses the same space for a forward table with a rule table; it uses hand-coded parsing, which is hard to read and modify...

But it has to comply with some constraints: it has to use ONLY the standard library, it must be completed in a short time.

Then I post it here, after some polishing, for a reference.

SimpleRouter.cpp:

#include <vector>
#include <string>
#include <iostream>
#include <sstream>

using namespace std;

class SimpleRouter
{
public:
typedef unsigned short num_type;
typedef pair<num_type, num_type> range_type;
typedef vector<range_type> ip_type;
typedef vector<ip_type> forward_table_type;
typedef pair<string, ip_type> rule_type;
typedef vector<rule_type> rule_table_type;

// This method & its signature is required by the problem.
vector<string> route(vector<string> rules, vector<string> packets);

private:
string route(ip_type& packet);

bool range_any(const range_type& ip_seg);
bool match(const ip_type& rule_ip, const ip_type& packet);

range_type parse_ip_seg(string& seg);
ip_type parse_ip(const string& ip);

string ip_seg_to_string(const range_type& ip_seg);
string ip_to_string(const ip_type& ip, char port_seperator);

string get_forward(const ip_type& rule, ip_type packet);

private:
const static num_type min_num = 0;
const static num_type max_num = static_cast<num_type>(-1);

rule_table_type rule_table;
forward_table_type forward_table;

void build_table(vector<string>& rules);

public:
// For debug
void print_table();
void print_ip(const ip_type& ip);

};

void SimpleRouter::build_table(vector<string>& rules)
{
rule_table.reserve(rules.size());
forward_table.reserve(rules.size());

string action, ip_str, port_str;
for(vector<string>::const_iterator iter = rules.begin();
iter != rules.end(); ++iter)
{
string::size_type end_action = iter->find_first_of(' ');
action = iter->substr(0, end_action);
ip_str = iter->substr(end_action + 1);

if(action == "FORWARD")
{
// get position of forward target
string::size_type temp = ip_str.find_first_of(' ') + 1;
string::size_type start_fwdip = ip_str.find_first_of(' ', temp) + 1;

rule_table.push_back(make_pair(action, parse_ip(ip_str.substr(0, start_fwdip - 1))));
forward_table.push_back(parse_ip(ip_str.substr(start_fwdip)));
}
else
{
rule_table.push_back(make_pair(action, parse_ip(ip_str)));
// push a placeholder to forward table
forward_table.push_back(ip_type());
}
}
}

vector <string> SimpleRouter::route(vector<string> rules, vector<string> packets)
{
build_table(rules);

vector<string> ret;
for(vector<string>::iterator iter = packets.begin();
iter != packets.end(); ++iter)
{
ret.push_back(route(parse_ip(*iter)));
}
return ret;
}

string SimpleRouter::route(ip_type& packet)
{
// check rules one-by-one reversely, as the last matched rule counts
forward_table_type::const_reverse_iterator fwd_iter = forward_table.rbegin();
rule_table_type::const_reverse_iterator rule_iter = rule_table.rbegin();
// workaround for a VC7.1 bug
rule_table_type::const_reverse_iterator rule_rend = rule_table.rend();

for(; rule_iter != rule_rend; ++rule_iter, ++fwd_iter)
{
string action = rule_iter->first;

if(action == "FORWARD")
{
if(match(rule_iter->second, packet))
{
return get_forward(*fwd_iter, packet);
}
}
else
{
if(match(rule_iter->second, packet))
return action;
}
}

// match no rules
return "REJECT";
}

string SimpleRouter::get_forward(const ip_type& fwd, ip_type packet)
{
// if fwd port is specified, replace packet port with fwd port
if(fwd.size() > 4 && !range_any(fwd[4]))
packet[4] = fwd[4];

return ip_to_string(packet, ':');
}

bool inline SimpleRouter::range_any(const range_type& ip_seg)
{
return ip_seg.first == min_num && ip_seg.second == max_num;
}

bool SimpleRouter::match(const ip_type& rule_ip, const ip_type& packet)
{
ip_type::const_iterator rule_iter = rule_ip.begin();
ip_type::const_iterator pk_iter = packet.begin();

for( ; rule_iter != rule_ip.end(); ++rule_iter, ++pk_iter)
{
// see if packet ip is within the range of rule
if(!(rule_iter->first <= pk_iter->first) ||
!(rule_iter->second >= pk_iter->second))
return false;
}
return true;
}

string SimpleRouter::ip_seg_to_string(const range_type& ip_seg)
{
char ret[12] = "*"; //the longest possible segment is 11 chars:
//xxxxx-xxxxx

if(!range_any(ip_seg))
{
int len = sprintf(ret, "%d", ip_seg.first);
if(ip_seg.first != ip_seg.second)
{
ret[len] = '-';
sprintf(ret + len + 1, "%d", ip_seg.second);
}
}

return ret;
}

string SimpleRouter::ip_to_string(const ip_type& ip, char port_seperator)
{
string ret;
ret.reserve(44); //the longest possible ip string is 43 chars:
//xxx-xxx.xxx-xxx.xxx-xxx.xxx-xxx xxxxx-xxxxx

ip_type::const_iterator iter = ip.begin();
for(int i = 0; iter != ip.end(); ++iter, ++i)
{
ret.append(ip_seg_to_string(*iter));
if(i < 3)
ret.append(".");
else if(i == 3)
ret.append(1, port_seperator);
}

return ret;
}

SimpleRouter::range_type SimpleRouter::parse_ip_seg(string& seg)
{
if(seg == "*")
{
return make_pair(min_num, max_num);
}
else
{
string::size_type dash_pos = seg.find_first_of('-');

num_type start, end;
if(dash_pos == string::npos)
{
stringstream(seg) >> start;
end = start;
}
else
{
stringstream(seg.substr(0, dash_pos)) >> start;
stringstream(seg.substr(++dash_pos)) >> end;
}

return make_pair(start, end);
}
}

SimpleRouter::ip_type SimpleRouter::parse_ip(const string& ip)
{
ip_type ret;

// break string ip into ip & port
string::size_type end_ip, start_port;

end_ip = ip.find_first_of(' ');
start_port = (end_ip == string::npos) ? string::npos : end_ip + 1;

// parse the 4 segment of ip
string::size_type start_seg = 0, end_seg;
for(int i = 0; i < 4; ++i)
{
end_seg = (i == 3) ? end_ip : ip.find_first_of('.', start_seg);
ret.push_back(parse_ip_seg(ip.substr(start_seg, end_seg - start_seg)));
start_seg = end_seg + 1;
}

// parse port(optional)
if(start_port != string::npos)
ret.push_back(parse_ip_seg(ip.substr(start_port)));

return ret;
}

//===================== For debug ======================
void SimpleRouter::print_ip(const ip_type& ip)
{
cout << ip_to_string(ip, ' ');
}

void SimpleRouter::print_table()
{
rule_table_type::iterator rule_iter = rule_table.begin();
forward_table_type::iterator fwd_iter = forward_table.begin();

for(; rule_iter != rule_table.end(); ++rule_iter, ++fwd_iter)
{
cout << rule_iter->first << " ";

print_ip(rule_iter->second);
if(!fwd_iter->empty())
{
cout << " ";
print_ip(*fwd_iter);
}

cout << endl;
}
}

//========================== Test ===========================
#include <iostream>
#include <algorithm>

template <class T>
struct ArraySize
{};

template <class T, int D>
struct ArraySize<T[D]>
{
static const int value = D;
};

template <class ARY>
void InitWith(vector<string>& vect, const ARY& ary)
{
int size = ArraySize<ARY>::value;

vect.reserve(size);
vect.insert(vect.begin(), ary, ary + size);
}

int main()
{
string rule_ary[] =
{
"REJECT *.20-252.114-157.36-91 13171-54085",
"ACCEPT *.*.73-180.* *",
"FORWARD 55.63.173.239 * 168.154.33.25",
"REJECT *.72-73.*.48-191 *",
"REJECT 20.51.*.* 4579",
"ACCEPT 70-166.*.*.86-182 *",
"REJECT 88-190.*.119-157.* 3316-27844",
"FORWARD *.52-221.134-250.66-207 * 116.94.120.82"
};

string packet_ary[] =
{
"203.11.104.45 44072",
"154.92.128.87 30085",
"20.51.68.55 4579",
"177.73.138.69 14319",
"112.65.145.82 26287",
"55.63.173.239 45899"
};

vector<string> rules, packets;
InitWith(rules, rule_ary);
InitWith(packets, packet_ary);

SimpleRouter router;
vector<string> ret = router.route(rules, packets);

for(vector<string>::iterator i = ret.begin(); i != ret.end(); ++i)
cout << *i << endl;

//============ debug =============
cout << endl << "Rule table, with forwards: " << endl;
router.print_table();
//================================
}

=================================================================================
cl /EHsc SimpleRouter.cpp

SimpleRouter

OUTPUT:

ACCEPT
ACCEPT
REJECT
177.73.138.69:14319
112.65.145.82:26287
55.63.173.239:45899

Rule table, with forwards:
REJECT *.20-252.114-157.36-91 13171-54085
ACCEPT *.*.73-180.* *
FORWARD 55.63.173.239 * 168.154.33.25
REJECT *.72-73.*.48-191 *
REJECT 20.51.*.* 4579
ACCEPT 70-166.*.*.86-182 *
REJECT 88-190.*.119-157.* 3316-27844
FORWARD *.52-221.134-250.66-207 * 116.94.120.82

你可能感兴趣的:(topcoder)