This is the refactored version of SimpleRouter. By using boost.tokenizer, the parsing of rules and packets get simplified. By using polymophic and boost ptr_container, adding new types of rules get much more easier. By encapsulating most "low level" details in Addr and AddrRange, the validation logic becomes much more clear. And finally, by using more precise datatypes, the size of rule table cuts to more than half.
SimpleRouter.cpp:
#include <vector>#include <string>#include <iostream>#include <limits>#include <algorithm>#include <memory>#include <boost/array.hpp>#include <boost/tokenizer.hpp>#include <boost/lexical_cast.hpp>#include <boost/ptr_container/ptr_vector.hpp>#include <boost/lambda/lambda.hpp>#include <boost/lambda/bind.hpp>using namespace std;using namespace boost;using namespace boost::lambda;typedef unsigned char ip_seg_type;typedef unsigned short port_type;typedef pair<ip_seg_type, ip_seg_type> ip_seg_range_type;typedef pair<port_type, port_type> port_range_type;typedef array<ip_seg_type, 4> ip_type;typedef array<ip_seg_range_type, 4> ip_range_type;typedef tokenizer<char_separator<char> > token;struct Addr{ Addr(const string& addr) { from_string(addr); } ip_type ip; port_type port; bool has_port; void from_string(const string& str); string to_string();};void Addr::from_string(const string& str){ char_separator<char> sep_ip_port(" "), sep_ip_seg("."); token tok_addr(str, sep_ip_port); token::const_iterator tok_addr_iter = tok_addr.begin(); string ip_str = *tok_addr_iter; has_port = ++tok_addr_iter != tok_addr.end(); token tok_ip(ip_str, sep_ip_seg);
transform(tok_ip.begin(), tok_ip.end(), ip.begin(),
bind(&atoi, bind(&string::c_str, _1))
);
if(has_port) port = atoi(tok_addr_iter->c_str());
}
string Addr::to_string()
{
string ret;
ret.reserve(22); //xxx.xxx.xxx.xxx xxxxx
for(ip_type::const_iterator iter = ip.begin();
iter != ip.end(); ++iter)
{
char buf[5];
sprintf(buf, "%d.", *iter);
ret.append(buf);
}
// remove last '.'
ret.erase(--ret.end());
if(has_port)
{
char buf[7];
sprintf(buf, " %d", port);
ret.append(buf);
}
return ret;
}
template <class T>
T parse_seg_range(const string& str)
{
if(str == "*")
{
return make_pair(numeric_limits<T::first_type>::min(),
numeric_limits<T::second_type>::max());
}
else
{
char_separator<char> sep("-");
token tok(str, sep);
token::const_iterator tok_iter = tok.begin();
T::first_type first = static_cast<T::first_type>(atoi(tok_iter->c_str()));
T::second_type second = ++tok_iter == tok.end() ? first :
static_cast<T::second_type>(atoi(tok_iter->c_str()));
return make_pair(first, second);
}
}
template <class T>
string seg_range_to_string(T& seg_range)
{
if(seg_range.first == numeric_limits<T::first_type>::min() &&
seg_range.second == numeric_limits<T::second_type>::max())
{
return "*";
}
else
{
char buf[12];
int len = sprintf(buf, "%d", seg_range.first);
if(seg_range.second != seg_range.first)
{
buf[len] = '-';
sprintf(buf + len + 1, "%d", seg_range.second);
}
return buf;
}
}
template <class Range, class Seg>
bool inline seg_covers(const Range& seg_range, const Seg& seg)
{
return seg_range.first <= seg && seg_range.second >= seg;
}
struct AddrRange
{
AddrRange(const string& str)
{ from_string(str); }
ip_range_type ip_range;
port_range_type port_range;
void from_string(const string& str);
string to_string();
bool covers(const Addr& addr);
};
void AddrRange::from_string(const string& str)
{
char_separator<char> sep_ip_port(" "), sep_ip_seg(".");
token tok_addr(str, sep_ip_port);
string ip_range_str = *tok_addr.begin();
token tok_ip_range(ip_range_str, sep_ip_seg);
ip_seg_range_type (*parser)(const string&) = &parse_seg_range<ip_seg_range_type>;
transform(tok_ip_range.begin(), tok_ip_range.end(), ip_range.begin(),
bind(parser, _1)
);
port_range = parse_seg_range<port_range_type>(*++tok_addr.begin());
}
string AddrRange::to_string()
{
string ret;
ret.reserve(44);
for(ip_range_type::const_iterator ip_range_iter = ip_range.begin();
ip_range_iter != ip_range.end(); ++ip_range_iter)
{
ret.append(seg_range_to_string(*ip_range_iter));
ret.append(1, '.');
}
// replace the last '.' with a ' '
*--ret.end() = ' ';
ret.append(seg_range_to_string(port_range));
return ret;
}
bool AddrRange::covers(const Addr& addr)
{
ip_type::const_iterator ip_iter = addr.ip.begin();
// function pointer to seg_covers.
// neither bind or boost.function can be used without a concern here.
bool (*range_covers_ip)(const ip_seg_range_type&, const ip_seg_type&) =
&seg_covers<ip_seg_range_type, ip_seg_type>;
bool ip_covered = find_if(ip_range.begin(), ip_range.end(),
!bind(range_covers_ip, _1, *var(ip_iter)++)
) == ip_range.end();
return ip_covered && seg_covers(port_range, addr.port);
}
class rule
{
public:
virtual string validate(const Addr& packet) = 0;
virtual string to_string() const = 0;
};
class accept_rule : public rule
{
public:
accept_rule(const string& str)
{ range_.reset(new AddrRange(str)); }
virtual string validate(const Addr& packet)
{ return range_->covers(packet) ? "ACCEPT" : ""; }
virtual string to_string() const
{ return string("ACCEPT ").append(range_->to_string()); }
private:
auto_ptr<AddrRange> range_;
};
class reject_rule : public rule
{
public:
reject_rule(const string& str)
{ range_.reset(new AddrRange(str)); }
virtual string validate(const Addr& packet)
{ return range_->covers(packet) ? "REJECT" : ""; }
virtual string to_string() const
{ return string("REJECT ").append(range_->to_string()); }
private:
auto_ptr<AddrRange> range_;
};
class forward_rule : public rule
{
public:
forward_rule(const string& str);
virtual string validate(const Addr& packet)
{ return range_->covers(packet) ? make_forward(packet) : ""; }
virtual string to_string() const
{ return string("FORWARD ").append(range_->to_string())
.append(" ").append(fwd_->to_string()); }
string make_forward(const Addr& packet);
private:
auto_ptr<AddrRange> range_;
auto_ptr<Addr> fwd_;
};
forward_rule::forward_rule(const string& str)
{
string::size_type pos = str.find_first_of(' ');
pos = str.find_first_of(' ', pos + 1);
range_.reset(new AddrRange(str.substr(0, pos)));
fwd_.reset(new Addr(str.substr(++pos)));
}
string forward_rule::make_forward(const Addr& packet)
{
bool fwd_has_port = fwd_->has_port;
if(!fwd_has_port)
{
fwd_->has_port = true;
fwd_->port = packet.port;
}
string ret = fwd_->to_string();
fwd_->has_port = fwd_has_port;
return ret;
}
auto_ptr<rule> make_rule(const string& str)
{
string::size_type pos = str.find_first_of(' ');
string action = str.substr(0, pos);
auto_ptr<rule> ret;
if(action == "ACCEPT")
ret.reset(new accept_rule(str.substr(++pos)));
else if(action == "REJECT")
ret.reset(new reject_rule(str.substr(++pos)));
else if(action == "FORWARD")
ret.reset(new forward_rule(str.substr(++pos)));
else
throw "Unexpected rule";
return ret;
}
class SimpleRouter
{
public:
// This method & its signature is required by the problem.
vector<string> route(vector<string> rules, vector<string> packets);
typedef vector<string> svect_type;
private:
string route_packet(const Addr& packet);
void build_rule_table(const svect_type& rules);
void print_rule_table();
private:
typedef ptr_vector<rule> rule_table_type;
rule_table_type rule_table_;
};
vector<string> SimpleRouter::route(vector<string> rules, vector<string> packets)
{
build_rule_table(rules);
svect_type ret;
for(svect_type::const_iterator iter = packets.begin();
iter != packets.end(); ++iter)
{
ret.push_back(route_packet(Addr(*iter)));
}
return ret;
}
string SimpleRouter::route_packet(const Addr& packet)
{
for(rule_table_type::reverse_iterator iter = rule_table_.rbegin();
iter != rule_table_.rend(); ++iter)
{
string ret = iter->validate(packet);
if(!ret.empty())
return ret;
}
return "REJECT";
}
void SimpleRouter::build_rule_table(const svect_type& rules)
{
rule_table_.reserve(rules.size());
for(svect_type::const_iterator iter = rules.begin();
iter != rules.end(); ++iter)
rule_table_.push_back(make_rule(*iter).release());
}
void SimpleRouter::print_rule_table()
{
for(rule_table_type::const_iterator iter = rule_table_.begin();
iter != rule_table_.end(); ++iter)
cout << iter->to_string() << 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_each(ret.begin(), ret.end(), cout << _1 << "\n");
}
=================================================================================
cl /EHsc SimpleRouter.cpp
SimpleRouter
ACCEPT
ACCEPT
REJECT
116.94.120.82 14319
116.94.120.82 26287
168.154.33.25 45899
There are still numerous places to improve. I'll come back to this some day later.
分享到:
相关推荐
Topcoder-App 该存储库包含来自tc站点存储库的所有新的topcoder页面或重构的Angular应用程序/页面。 使用的技术是Jade,SCSS,Angular和Gulp。安装如果没有安装指南针,请运行以下命令: Windows-Gem安装指南针Linux...
Topcoder X应用 要求 需要Nodejs 8 MongoDB 3.2 卡夫卡 nodemon(用于本地开发) 安装依赖项 npm install 源代码lint eslint用于加载javascript源代码: npm run lint 终点 GET / github / owneruser / login-...
要求 需要Nodejs 8 安装依赖项 npm install 源代码lint eslint用于加载javascript源代码: ... 使用上面使用的topcoder用户名和 转到设置,并确保正确设置了git主机,如果没有,请单击setup并授权进
topcoder UML 工具我们很自豪地介绍 TopCoder UML 工具:一个易于使用、一致的建模工具,用于设计和开发竞赛。 新工具完全由 TopCoder 社区设计和开发,用于对序列、类、用例和活动图进行建模。 最重要的是,所有...
用于topcoder的第3方编辑器插件。
TopCoder(包括递推关系和主定理): 数据结构 数组 实现一个自动调整大小的向量。 描述: (从15m 32s开始观看) 实现一个向量(具有自动调整大小的可变数组): 练习使用数组和指针进行编码,以及使用指针数学跳转...
要求 需要Nodejs 8 DynamoDB 安装依赖项 npm install 源代码lint eslint用于加载javascript源代码: npm run lint 配置 支持以下配置参数,它们在config/default.js中定义,可以在系统环境中配置... topcoder的基本U
【标题】"topcoder-srm:Topcoder SRM竞赛解决方案" 这个标题暗示了这是一个与Topcoder平台上的Single Round Matches (SRM)竞赛相关的资源集合。Topcoder SRM是全球知名的在线编程竞赛,参赛者需要在限定时间内解决...
娱乐与学习TopCoder Swift挑战-TableView包含plist数据,后跟URL中的JSON数据挑战概述对于此挑战,您将使用tableView组件以表格格式显示所有调查记录。技术要求您需要创建使用tableView组件以显示虚拟调查plist数据...
发展# Install dependenciesnpm install# Run test & buildnpm testnpm run build# Go to other project which depends on the topcoder-react-lib, config its package.json so# that the 'topcoder-react-lib' ...
TopCoder 练习 一个 Eclipse 项目,它将包含我用 Java 编写的 TopCoder 的所有实践问题。 包命名约定 会话类型,后跟其 ID 号和分区。 类型###.DIV# Eclipse 版本信息 版本:Luna Service Release 1 (4.4.1) 版本...
TopCoder 黑客地球 黑客排名 Leetcode 脸书 谷歌 CSA学院 3种类型的比赛分为3条不同的路线。 他们每个人都用颜色编码,以更好地理解用户。 每个竞赛都有一个链接,您可以通过其直接访问特定的编程网站 建立 这个...
支持的站点:-Codeforces -Codeforces :: Gym-TopCoder-AtCoder-CS Academy-CodeChef-HackerRank-HackerEarth-Kick Start-LeetCode-A2OJ Credit进入kontests.net,用于通过其API提供竞赛数据。 支持语言:English
topcoder-dl :bookmark: 下载所有Topcoder数据科学教程,并另存为PDF 灵感来自这个很棒的东西,叫做 安装 要手动安装的依赖项 从源代码构建 git clone " https://github.com/tushar-rishav/topcoder-dl.git " cd ...
topcoder Challenge API 最初是使用构建的。 路由是通过使用 api\swagger\swagger.yaml 中的 swagger 配置文件来处理的。 路由是使用和模块完成的。 构建状态 工匠 快速入门(本地运行) 启动 postgres 创建...
【标题】:“topcoder:topcoder问题” 在编程竞赛领域,TopCoder是一个广为人知...而“topcoder-master”这样的资源则为学习者提供了一个实际操作、学习他人解题思路的宝贵资料库,帮助他们在编程旅程中不断攀登高峰。
Topcoder支付工具 基于 。 要在开发模式下安装,测试和运行: $ npm install$ npm test# NODE_CONFIG_ENV can be "development" or "production". It has no influence on# the code build and execution mode, it ...
- **X雨喆** (CTO): 前微软亚洲研究院工程师,曾担任ACM国际程序设计大赛高级教练,Topcoder开发者国际排名前200。 - **王博男** (设计总监): 前光线传媒设计师,参与多部电影全案宣传设计,如2013年央视春晚视觉识别...
【标题】"topcoder-python:用 Python 编写的 TopCoder 解决方案" 是一个项目,其核心在于使用 Python 语言解决 TopCoder 平台上的算法竞赛问题。TopCoder 是一个著名的在线编程竞赛平台,它提供了丰富的算法挑战,...