[c++/HTTP] 路由的 前缀树和哈希表 实现

/ 6,584评论 / 25386阅读 / 5点赞

路由

基础功能

思考|数据结构设计

前缀树|字典树

哈希表

结合前缀树和哈希表

实现思路

c++实现

#ifndef HTTPMETHOD_H
#define HTTPMETHOD_H
#include <string>

//http请求类型枚举
class MyHttpMethod_e {
public:
	static constexpr int 
		HttpMethod_size = 8,	//http请求类型的数量
		Get		= 1,
		Post	= 2,
		Put		= 4,
		Delete	= 8,
		Head	= 16,
		Options	= 32,
		Trace	= 64,
		Connect	= 128,
		Min		= MyHttpMethod_e::Get,
		Max		= MyHttpMethod_e::Connect;
};

class MyHttpMethodArrItem_s {
public:
	int				mNum;	//在数组中的下标
	int				mInt;	//method的枚举数值
	std::string		mStr;	//类型字符串名称
	MyHttpMethodArrItem_s(int in_mNum, int in_mInt, const std::string& in_mStr) {
		mNum = in_mNum;
		mInt = in_mInt;
		mStr = in_mStr;
	}
};
//HTTP请求类型数组,便于循环判断
static const MyHttpMethodArrItem_s myHttpMethodArr[MyHttpMethod_e::HttpMethod_size] = {
	MyHttpMethodArrItem_s(0, MyHttpMethod_e::Get,		"GET"),
	MyHttpMethodArrItem_s(1, MyHttpMethod_e::Post,		"POST"),
	MyHttpMethodArrItem_s(2, MyHttpMethod_e::Put,		"PUT"),
	MyHttpMethodArrItem_s(3, MyHttpMethod_e::Delete,	"DELETE"),
	MyHttpMethodArrItem_s(4, MyHttpMethod_e::Head,		"HEAD"),
	MyHttpMethodArrItem_s(5, MyHttpMethod_e::Options,	"OPTIONS"),
	MyHttpMethodArrItem_s(6, MyHttpMethod_e::Trace,		"TRACE"),
	MyHttpMethodArrItem_s(7, MyHttpMethod_e::Connect,	"CONNECT")
};
//HTTP请求类型的相关操作方法
class MyHttpMethod_c {
public:
	//判断in_m1与in_m2包含的请求类型是否有交集
	static bool isAllow(int in_m1, int in_m2);
	static bool isAllow(const std::string& in_m1, int in_m2);
	static bool isAllow_strOne(const std::string& in_m1, int in_m2);
	static bool isAllow(const std::string& in_m1, const std::string& in_m2);

	//将包含请求类型的in_str转为int值表示
	static int strToInt(const std::string& in_str);
	/*将包含请求类型的in_str转为int值表示
		*	但要求in_str只包含了一个请求类型
		*	否则只返回按HttpMethod_e枚举顺序第一个找到的类型对应的int值
	*/
	static int strToInt_one(const std::string& in_str);
	//将使用int值表示的请求类型转为string表示
	static std::string intToStr(int in_int);
	/*将使用int值表示的请求类型转为string表示
		*	但要求in_int只表示一个请求类型
		*	否则只返回按HttpMethod_e枚举顺序第一个找到的类型对应的string
	*/
	static std::string intToStr_one(int in_int);
	static int toIndex_one(int in_method);
	static int toIndex_one(const std::string& in_method);
};

#endif
#ifndef MYROUTER_H
#define	MYROUTER_H

#include <map>
#include <string>

#include "MyHttpTask.h"
#include "MyHttpMethod.h"

//路由
class MyRouter_c {
protected:
	//路由 树节点
	struct RouterTreePort_s {
	protected:
	public:
		/// <summary>
		///  路径
		/// </summary>
		std::string			path;
		/// <summary>
		/// 对应Http方法的处理函数
		/// </summary>
		MyHttpProcess_t		fun[MyHttpMethod_e::HttpMethod_size];
		/// <summary>
		/// 子节点
		/// </summary>
		std::map<std::string, RouterTreePort_s*> child;
		
		RouterTreePort_s(const std::string& in_path = "") noexcept :path(in_path) {
			for (int i = MyHttpMethod_e::HttpMethod_size; i-- > 0;)
				fun[i] = nullptr;
		}
		// 返回对应路径节点,不存在则返回nullptr
		RouterTreePort_s* get_child(const std::string& in_path, std::string& re_path, bool do_add = false) {
			return this->get_child(in_path.c_str(), re_path, do_add);
		}
		/// <summary>
		/// 获取对应路径[in_path]的节点,并返回路由路径到[re_path]
		/// </summary>
		/// <param name="in_path">待查找路径</param>
		/// <param name="re_path">节点真实路由路径;无对应节点时返回空</param>
		/// <param name="do_add">当路径对应节点不存在时,是否添加节点</param>
		/// <returns>对应路径节点,不存在则返回nullptr</returns>
		RouterTreePort_s* get_child(const char* in_path, std::string& re_path, bool do_add = false) {
			const char* strptr = in_path, *nextptr = in_path;
			for (;;) {
				if (*nextptr == '/') {
					if (*(nextptr + 1) == '/') {
						++nextptr;
					}
					else
						break;
				}
				else if (*nextptr != '\0') {
					++strptr;
					++nextptr;
				}
				else {	//strp = '\0',是最后一个子节点
					auto it = child.find(in_path);
					if (it != child.end()) {	//存在子节点
						re_path = in_path;
						return it->second;
					}
					else {
						if (do_add) {
							auto treeptr = new RouterTreePort_s(in_path);
							child[in_path] = treeptr;
							re_path = in_path;
							return treeptr;
						}
						else {
							it = child.find("*");
							if (it != child.end()) {
								re_path = "*";
								return it->second;
							}
							else {
								re_path.clear();
								return nullptr;
							}
						}
					}
				}
			}
			// *strp = '/',不是最后一个子节点
			std::string str{ in_path, strptr };
			++nextptr;
			auto it = child.find(str);
			if (it != child.end()) {	//如果存在子节点
				auto re_ptr = it->second->get_child(nextptr, re_path, do_add);
				if (re_ptr != nullptr) {	//有找到匹配的路径
					re_path = str + "/" + re_path;
				}
				return re_ptr;
			}
			else {
				if (do_add) {		//如果需要新建子节点
					auto treeptr = new RouterTreePort_s(str);
					child[str] = treeptr;
					auto re_ptr = treeptr->get_child(nextptr, re_path, do_add);
					if (re_ptr != nullptr) {
						re_path = str + "/" + re_path;
					}
					return re_ptr;
				}
				else {		//查找是否有通配符
					it = child.find("*");
					if (it != child.end()) {
						re_path = "*";
						return it->second;
					}
					else {	// 无查找结果,清空re_path并返回nullptr
						re_path.clear();
						return nullptr;
					}
				}
			}
		}
		/* 使用类型枚举设置处理函数
		* 支持同时设置多个类型
		*/
		bool set_fun_byMethod(const MyHttpProcess_t& in_fun, int in_method) {
			size_t donum = 0;	//记录设置函数的次数
			for (int i = 0; i < MyHttpMethod_e::HttpMethod_size; ++i) {
				if (MyHttpMethod_c::isAllow(in_method, myHttpMethodArr[i].mInt)) {
					fun[myHttpMethodArr[i].mNum] = in_fun;
					++donum;
				}
			}
			if (donum > 0)
				return true;
			else
				return false;
		}
		/* 使用类型下标设置处理函数
		* 仅支持一个类型下标
		*/
		bool set_fun_byIndex(const MyHttpProcess_t& in_fun, int in_index) {
			if (in_index >= 0 && in_index < MyHttpMethod_e::HttpMethod_size) {
				fun[in_index] = in_fun;
				return true;
			}
			else
				return false;
		}
		/* 用数组下标获取函数
		* 下表越界时返回nullptr
		* 指定下标的函数未定义时返回nullptr
		*/
		const MyHttpProcess_t& get_fun_byIndex(int in_index) const {
			if (in_index >= 0 && in_index < MyHttpMethod_e::HttpMethod_size) {
				return fun[in_index];
			}
			else
				return Nullptr_MyHttpProcess;
		}
		/* 用请求类型获取函数
		* 指定请求类型对应的函数未定义时返回nullptr
		*/
		const MyHttpProcess_t& get_fun_byMethod(int in_method) const {
			return this->get_fun_byIndex(MyHttpMethod_c::toIndex_one(in_method));
		}
		/* 清空子节点
		*/
		void clear_child() {
			for (auto it = child.begin(); it != child.end(); ) {
				it->second->clear_child();		//调用子节点的清理
				delete (it->second);			//释放子节点
				child.erase(it);
				it = child.begin();				//重置it的指向
			}
		}
		~RouterTreePort_s() {
			this->clear_child();
		}
	};
	struct RouterCacheValue_s {
		/// <summary>
		/// 路由路径
		/// </summary>
		std::string router_path;
		/// <summary>
		/// 对应节点
		/// </summary>
		RouterTreePort_s* treeptr = nullptr;
	};

	//路由字典树
	RouterTreePort_s routerTree;
	/* 哈希缓存
	* 如果使用缓存接口获取路由位置,获取成功将留下缓存
	* 下次使用相同的path获取时直接提取缓存
	* 添加或删除路由位置将清空缓存重新生成
	*/
	std::unordered_map<std::string, RouterCacheValue_s> cacheMap;
	/// <summary>
	/// 不经过缓存,直接搜索对应路径[in_path]、对应Http方法[in_method]的节点
	/// </summary>
	/// <param name="in_path">待查找路径</param>
	/// <param name="re_path">返回该节点的真实路由路径;节点不存在时返回空</param>
	/// <returns>返回查找结果节点</returns>
	RouterTreePort_s* get_treep_nocache(const std::string& in_path, std::string& re_path);

public:
	MyRouter_c() noexcept :routerTree("/") {}

	/* 添加路由
	* 允许使用通配符 *
	* 允许同时设置多个类型枚举
	* 路径中连续的 / 将被视为仅一个 /
		* 即 /a//b///c 等同于 /a/b/c
	*/
	bool add(const std::string& in_path, int in_method, const MyHttpProcess_t& in_fun);
	/* 判断是否存在路由位置,使用缓存
		* 有,且对应方法有处理函数:返回其处理函数指针,并赋值re_path
		* 有,但对应方法没有处理函数:返回nullptr,并赋值re_path
		* 没有:	返回nullptr,re_path置空
	*/
	const MyHttpProcess_t& get(const std::string& in_path, int in_method, std::string& re_path);
	/* 判断是否存在路由位置,不使用缓存
	* 有,且对应方法有处理函数:返回其处理函数指针,并赋值re_path
	* 有,但对应方法没有处理函数:返回nullptr,并赋值re_path
	* 没有:	返回nullptr,re_path置空
	*/
	const MyHttpProcess_t& get_nocache(const std::string& in_path, int in_method, std::string& re_path);
	/*移除指定的路由位置,并返回其处理函数
	* \param in_method_one 一次调用仅支持移除单一请求类型对应的处理函数
	*/
	MyHttpProcess_t remove(const std::string& in_path, int in_method_one);
	/* 清理缓存
	*/
	void clear_cache();
	//清空路由
	void clear();

};

#endif // ! MYROUTER_H
#include "MyRouter.h"

using std::string;
using std::map;

bool MyRouter_c::add(const string& in_path, int in_method, const MyHttpProcess_t& in_fun)
{
	string&& method = MyHttpMethod_c::intToStr(in_method);
	if (method.empty() == false){		//检查method
		this->cacheMap.clear();
		const char* strp = in_path.c_str();
		while (*strp == '/')
			++strp;
		string re_path{};
		auto treep = this->routerTree.get_child(strp, re_path ,true);
		return treep->set_fun_byMethod(in_fun, in_method);
	}
	else
		return false;
}
MyRouter_c::RouterTreePort_s* MyRouter_c::get_treep_nocache(const std::string& in_path, std::string& re_path) {
	const char* strp = in_path.c_str();
	while (*strp == '/')
		++strp;
	auto treep = this->routerTree.get_child(strp, re_path);
	if (false == re_path.empty()) {
		re_path = "/" + re_path;
	}
	return treep;
}
const MyHttpProcess_t& MyRouter_c::get_nocache(const std::string& in_path, int in_method, std::string& re_path) {
	auto treep = this->get_treep_nocache(in_path, re_path);
	if (treep != nullptr) {		//判断是否存在路由位置
		const auto& fun = treep->get_fun_byMethod(in_method);
		return fun;
	}
	return Nullptr_MyHttpProcess;
}
const MyHttpProcess_t& MyRouter_c::get(const std::string& in_path, int in_method, string& re_path)
{
	auto refind = this->cacheMap.find(in_path);
	MyRouter_c::RouterTreePort_s* treeptr = nullptr;
	if (refind != this->cacheMap.end()) {
		treeptr = refind->second.treeptr;
		re_path = refind->second.router_path;
	}
	else
		treeptr = this->get_treep_nocache(in_path, re_path);
	if (treeptr != nullptr) {		//判断是否存在路由位置
		const auto& fun = treeptr->get_fun_byMethod(in_method);
		if (fun) {
			this->cacheMap.insert(
				{
					in_path,
					{
						re_path,
						treeptr
					}
				});
		}
		return fun;
	}
	return Nullptr_MyHttpProcess;
}
MyHttpProcess_t MyRouter_c::remove(const std::string& in_path, int in_method) {
	string&& method = MyHttpMethod_c::intToStr(in_method);
	if (method.empty() == false) {		//检查method
		const char* strp = in_path.c_str();
		while (*strp == '/')
			++strp;
		string re_path{};
		auto treep = this->routerTree.get_child(strp, re_path, false);
		if (treep) {
			this->cacheMap.clear();
			int index = MyHttpMethod_c::toIndex_one(in_method);
			const auto& fun = treep->get_fun_byIndex(index);
			treep->set_fun_byIndex(nullptr, index);
			return fun;
		}
	}
	return nullptr;
}
void MyRouter_c::clear_cache() {
	this->cacheMap.clear();
}
void MyRouter_c::clear()
{
	this->routerTree.clear_child();
}
  1. Jamiescact说道:

    https://indianpharmacyeasy.com/# indian pharmacy paypal

  2. profi-teh-remont说道:

    Сервисный центр предлагает ремонт fly sl140 ds цены ремонт fly sl140 ds

  3. Jefferyswava说道:

    best erection pills: canadian pharmacy drugs gate – best medication for ed

  4. GlennSeade说道:

    over the counter ed medication Canadian pharmacy best prices new treatments for ed

  5. LarryHer说道:

    best ed supplements: canadian drugs gate – tadalafil without a doctor’s prescription

  6. Jamiescact说道:

    https://mexicanpharmgate.com/# pharmacies in mexico that ship to usa

  7. GlennSeade说道:

    erectile dysfunction natural remedies Canadian pharmacy best prices ed causes and treatment

  8. Jefferyswava说道:

    best india pharmacy: Indian pharmacy international shipping – indian pharmacy

  9. Jamiescact说道:

    http://canadiandrugsgate.com/# treatment with drugs

  10. Тут можно преобрести сейф охотничий купить ящик для оружия купить

  11. Тут можно преобрести сейф огнеупорный купить сейф огнеупорный купить

  12. Jamiescact说道:

    https://indianpharmacyeasy.com/# top 10 online pharmacy in india

  13. GlennSeade说道:

    male erectile dysfunction Canadian pharmacy best prices cheap pills online

  14. Jamiescact说道:

    https://mexicanpharmgate.com/# mexican border pharmacies shipping to usa

  15. Bradleyarrip说道:

    https://mexicanpharmgate.com/# mexican rx online

  16. LarryHer说道:

    medication from mexico pharmacy: Mexican Pharmacy Gate – reputable mexican pharmacies online

  17. Тут можно преобрести сейфы от пожара огнестойкий сейф купить

  18. TNEIZYO说道:

    Daisies. Lisa marie presley. Apollo. Sandra day o’connor. National gallery of art. Tito. Askew. Dab. Smells like teen spirit. Country. Quinta brunson. Sabres. Planned parenthood. Tourette syndrome. Naomie harris. Metropolitan museum of art. Penelope cruz. Cooper’s hawk. Civil engineering. Define the. Root. Anunnaki. Redwood forest. Frida. Smart watch. Gaslighting meaning. Chess board and. Feud capote vs the swans. Nut. Homicide. Panic attack. Adjective examples. Breitbart. Everwood. Ford vs ferrari. Iceland volcano eruption. Soul food. Ally financial. Animated movies. Torah. Africa cup. Modesto. Theology. The replacements. Turner syndrome. Planets. Refrain. Starry night. Jericho. Alphabet. Apricot. https://jajlwtk.filmfilmfilm.eu/MXGOC.html
    Love actually. Countries. Lysine. Veterans day. Saint lucia. Systolic blood pressure. Divvy. Pe. Apostle. Horizontal. What is meningitis. Presbyopia. Bogota. Guy ritchie. I love lucy. Thistle. Clandestine. Key. Playing cards. Alveoli. What is love. Brown v board of education. Patriot act. B-52. Strawberries. Martin starr. Relevant. Tokyo. Ease. Ken griffey jr. Gila monster. Haliburton. Liberty. Art garfunkel. Atlantic ocean. Democratic underground. Special olympics. As roma. Helen mccrory. Celia cruz. Butler university. French restaurant. Ticks. Spirited. Frasier cast. Drew carey. What language did jesus speak. Greta gerwig movies.

  19. Jamiescact说道:

    https://indianpharmacyeasy.com/# pharmacy website india

  20. Jamiescact说道:

    http://indianpharmacyeasy.com/# top 10 online pharmacy in india

  21. Тут можно преобрести купить противопожарный сейф сейфы пожаростойкие

  22. Bradleyarrip说道:

    https://canadiandrugsgate.com/# online meds for ed

  23. Bradleyarrip说道:

    https://canadiandrugsgate.com/# ed cures that actually work

  24. GlennSeade说道:

    herbal ed treatment Canada pharmacy online how to treat ed

  25. Jamiescact说道:

    http://indianpharmacyeasy.com/# indian pharmacy online

  26. Jamiescact说道:

    https://canadiandrugsgate.com/# best ed pills that work

  27. Jamiescact说道:

    http://mexicanpharmgate.com/# п»їbest mexican online pharmacies

  28. Тут можно преобрести сейф огнестойкий в москве купить огнестойкий сейф

  29. Тут можно преобрести купить сейф для охотничьего ружья купить шкаф оружейный

  30. Тут можно преобрести оружейные сейфы для ружей купить сейф для охотничьего ружья

  31. DennisBob说道:

    ED meds online: erepharm.com – ere pharm

  32. profi-teh-remont说道:

    Сервисный центр предлагает ремонт платы panasonic lumix dmc-sz10 ремон объектива panasonic lumix dmc-sz10

  33. Lloydlania说道:

    http://rybpharm.com/# rybpharm rybelsus

  34. RobertCaw说道:

    buy gabapentin online: buy gabapentin india – buy gabapentin online

  35. DennisBob说道:

    rybpharm rybelsus: buy rybelsus online usa – rybpharm

  36. RobertCaw说道:

    Kamagra Kam Pharm: kam pharm shop – buy kamagra oral jelly Kam Pharm

  37. CurtisTraup说道:

    buy kamagra oral jelly Kam Pharm kamagra Kamagra Kam Pharm

  38. RobertCaw说道:

    buy rybelsus rybpharm: buy rybelsus online usa – buy rybelsus online usa

  39. Тут можно преобрести сейф противопожарный купить купить сейф противопожарный

  40. CurtisTraup说道:

    cheapest lasix buy lasix fur pharm buy furosemide online

  41. DennisBob说道:

    rybpharm: buy rybelsus online usa – buy rybelsus canada

  42. Lloydlania说道:

    https://erepharm.com/# ED meds online with insurance

  43. Lloydlania说道:

    http://kampharm.shop/# Kamagra Kam Pharm

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注