go路由httprouter中的压缩字典树算法图解及c++实现

目录

  • go路由httprouter中的压缩字典树算法图解及c++实现

    • 前言
    • httprouter简介
    • 压缩字典树
      • 概念
      • 插入操作
      • 查询操作
      • c+++实现

go路由httprouter中的压缩字典树算法图解及c++实现

@


前言

准备从嵌入式往go后端转,今年准备学习一下gin框架,决定先从这个轻量级的路由请求器着手,本文讲讲它用到的压缩字典树算法。

httprouter简介

HttpRouter是一个Go编写的轻量级的高性能Http请求路由器(也可称为多路选择器multiplexer简称mux)

与Go的net/http包的默认mux不同,该路由器支持路由中的变量与请求方法进行匹配,同时具有很好的伸缩性。

该路由具有高性能的同时,也优化了内存的占用,即是是很长的路径和大量的路径,他都能很好的扩展,采用压缩字典树(基数树)结构实现高效的匹配。

压缩字典树

概念

压缩字典树,是trie树的一种,也称单词查找树、前缀树,善于进行字符串的检索、取字符串最长公共前缀、以及排序,常应用在搜索引擎中例如百度输入可能自动弹出能匹配到的单词出来.

压缩tire和标准trie最大的不同点就节点的数量与插入字符串的个数成正比,而不是与字符串的长度成正比,所以当字符串数量越来越多,越密集且相似度极高的情况下,会退化成标准trie树。

下面分别是/,/bear,/bell,/bid,/bull,/buy,/sell,/stock,/stop 的标准tire 和压缩 tire

插入操作

下面图解一串子串插入压缩trie过程,/,/serach,/support,/blog , 在httprouter上截的一段例子,我们只插到/blog

插入/

插入/serach

插入/support

插入/blog

查询操作

查询比较简单,后面看代码也比较快。
1、先找共同前缀。
2、再找目录。
3、循环上面两步,知道当前path相等。

c+++实现

这里注册了4个路径的回调函数,addRoute 即是插入操作,handler即是查询。

// httprouter.hpp

#pragma once
#include <string>
#include <vector>
#include <functional>
#include <memory>

namespace httprouter{

typedef std::function<void(void)> handler_t;

typedef struct _tree_node {
    std::string                                     path;
    std::string                                     indices;
    std::vector<std::shared_ptr<struct _tree_node>> children;
    handler_t                                       handle;
}tree_node_t;

class node
{
public:
    //! ctor
    node();

    //! dtor
    ~node(void);

    //! copy ctor
    node(const node&) = delete;
    //! assignment operator
    node& operator=(const node&) = delete;

    //! addRouter adds a node with the given handle to the path
    //! Not concurrency-safe!
    void addRoute(std::string path, handler_t handle);

    //! get path handler
    handler_t handler(std::string path);

private:
    void insertChild(tree_node_t* node, std::string& path, handler_t handle);

private:
    std::shared_ptr<tree_node_t> node_;
};

}

// httprouter.cpp
#include <algorithm>

#include "httprouter.hpp"

using namespace httprouter;

node::node()
    :node_(new tree_node_t{
      path:       "",
      indices:    "",
      children:   {},
      handle:     nullptr,
        })
{
}

node::~node(){

}

void node::addRoute(std::string path, handler_t handle) {
    std::string fullPath = path;
    auto node = node_;

    // no-empty tree
    if (node->path.size() > 0 || node->children.size() > 0) {
        while (true) {
            bool have_indices = false;

            //find the longest common prefix.
            std::size_t i = 0;
            auto max = std::min(node->path.size(), path.size());
            for (; i < max && path[i] == node->path[i];) {
                i++;
            }

            // Split edge
            if (i < node->path.size()) {
                auto child = std::shared_ptr<tree_node_t>(new tree_node_t{
                  path :      std::string(node->path.c_str() + i),
                  indices :   node->indices,
                  children :  std::move(node->children),
                  handle :    node->handle,
                });

                node->children = std::vector<std::shared_ptr<tree_node_t>>{ child };
                node->indices = std::string(node->path.c_str() + i, 1);
                node->path = std::string(path.c_str(), i);
                node->handle = nullptr;
            }

            // make new node a child of this node
            if (i < path.size()) {
                path = std::string(path.c_str() + i);

                char ch = path[0];

                // Check if a child with the next path byte exists
                for (std::size_t i = 0; i < node->indices.size(); i++) {
                    if (ch == node->indices[i]) {
                        //i = node.incrementChildPrio(i);
                        node = node->children[i];
                        have_indices = true;
                        break;
                    }
                }
                if (have_indices) {
                    continue;
                }

                //otherwise insert it
                if (ch != ':' && ch != '*') {
                    node->indices += ch;
                    auto child = std::shared_ptr<tree_node_t>(new tree_node_t{
                    path :      "",
                    indices :   "",
                    children :  {},
                    handle :    nullptr,
                    });
                    node->children.push_back(child);
                    node = child;
                }
                insertChild(node.get(), path, handle);
                return;
            }
            else if (i == path.size()) {
                if (node->handle) {
                    printf("error ! handle already exists.");
                    exit(1);
                }
                node->handle = handle;
            }
            return;
        }
    }
    else { // Empty tree
        insertChild(node.get(), path, handle);
    }

}

void node::insertChild(tree_node_t* node, std::string& path, handler_t handle) {
    node->path = std::string(path.c_str());
    node->handle = handle;
}

handler_t node::handler(std::string path) {
    auto node = node_;
    while (true) {
        if (path.size() > node->path.size()) {
            if (std::string(path.c_str(), node->path.size()) == node->path) {
                path = std::string(path.c_str() + node->path.size());
            }

            char ch = path[0];
            for (std::size_t i = 0; i < node->indices.size(); i++) {
                if (ch == node->indices[i]) {
                    node = node->children[i];
                    continue;
                }
            }
            // handle wildcard child
            // fix me
        }
        else if (path == node->path) {
            return node->handle;
        }
    }
}

//main.cpp

#include "httprouter.hpp"
#include <iostream>

void hello1() {
    std::cout << "hello1" << std::endl;
}
void hello2() {
  std::cout << "hello2" << std::endl;
}
void hello3() {
  std::cout << "hello3" << std::endl;
}
void hello4() {
  std::cout << "hello4" << std::endl;
}
void hello5() {
  std::cout << "hello5" << std::endl;
}

int main() {

  httprouter::node no;

  no.addRoute("/", hello1);
  no.addRoute("/serach/", hello2);
  no.addRoute("/support/", hello3);
  no.addRoute("/blog/", hello4);

  no.handler("/")();
  no.handler("/serach/")();
  no.handler("/support/")();
  no.handler("/blog/")();

}

结果:

节点信息:

原文地址:https://www.cnblogs.com/ailumiyana/p/10799487.html

时间: 2024-08-28 19:23:32

go路由httprouter中的压缩字典树算法图解及c++实现的相关文章

巧用字典树算法,轻松实现日志实时聚类分析

by 田富龙日志分析对于企业运维来说尤为重要,运维人员如不能实时了解服务器的安全状况,会给企业造成难以估计的损失.对日志进行分析,不仅可以了解到软.硬件设备的运行状况,还可以了解到报错日志的源头.服务器上正在发生的安全事件,判断错误是由应用引发的还是系统本身引起的,从而及时进行补救,以提高企业软.硬件设备的高可用性.然而,随着服务器数量逐渐增加,日志数据也与日俱增,面对这种境况,利用传统的方式对日志进行分析,显然已经不能满足企业的要求.此时,基于AI技术的日志分析方式就显得尤为重要.本文提出的实

bat中rar压缩命令

bat中rar压缩命令 数据库备份,导出的dmp 文件比较大,需要压缩,压缩后大小能变为原来十分之一左右吧. 写的是批处理的语句,每天调用,自动导出dmp 文件,压缩删除原文件. 首先写下路径,先将压缩软件的路径写入系统的环境变量里,加入到path中.比如将"C:\Program Files\WinRAR\"加入到path中. 之后批处理中写好相应的处理命令: rar a -df %filename%.rar %filename%.dmp 附: 压缩参数 用法:     rar <

Trie字典树算法

特性 Trie树属于树形结构,查询效率比红黑树和哈希表都要快.假设有这么一种应用场景:有若干个英文单词,需要快速查找某个单词是否存在于字典中.使用Trie时先从根节点开始查找,直至匹配到给出字符串的最后一个节点.在建立字典树结构时,预先把带有相同前缀的单词合并在同一节点,直至两个单词的某一个字母不同,则再从发生差异的节点中分叉一个子节点. 节点结构:每个节点对应一个最大可储存字符数组.假设字典只存26个小写英文字母,那么每个节点下应该有一个长度为26的数组.换言说,可存的元素类型越多,单个节点占

【Redis源代码剖析】 - Redis内置数据结构之压缩字典zipmap

原创作品,转载请标明:http://blog.csdn.net/Xiejingfa/article/details/51111230 今天为大家带来Redis中zipmap数据结构的分析,该结构定义在zipmap.h和zipmap.c文件里.我把zipmap称作"压缩字典"(不知道这样称呼正不对)是因为zipmap利用字符串实现了一个简单的hash_table结构,又通过固定的字节表示节省空间. zipmap和前面介绍的ziplist结构十分相似,我们能够对照地进行学习: Redis中

【Redis源码剖析】 - Redis内置数据结构值压缩字典zipmap

原创作品,转载请标明:http://blog.csdn.net/Xiejingfa/article/details/51111230 今天为大家带来Redis中zipmap数据结构的分析,该结构定义在zipmap.h和zipmap.c文件中.我把zipmap称作"压缩字典"(不知道这样称呼正不正确)是因为zipmap利用字符串实现了一个简单的hash_table结构,又通过固定的字节表示节省空间.zipmap和前面介绍的ziplist结构十分类似,我们可以对比地进行学习: Redis中

Android 中图片压缩分析(上)

作者: shawnzhao,QQ音乐技术团队一员 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是质量压缩,其二是下采样压缩. 前者是在不改变图片尺寸的情况下,改变图片的存储体积,而后者则是降低图像尺寸,达到相同目的. 由于本文的篇幅问题,分为上下两篇发布. 二.Android 质量压缩逻辑 在Android中,对图片进行质量压缩,通常我们的实现方式如下所示: ByteArrayOutputStream outputStream = new Byte

linux中的压缩命令详细解析(二)

我们在<Linux中的压缩命令详细解析(一)>中已经讲解了常见的三种压缩命令,下面我们开始讲解工作中最常用到的tar命令. 为了使压缩和解压缩变得简单,tar命令就应运而生了.那么究竟该如何使用呢? tar.gz格式: 压缩命令: tar -zcvf 压缩文件名 源文件名 举例: 把abc文件压缩成后缀为tar.gz格式的文件 tar -zcvf abc.tar.gz abc 解压缩命令: 举例:解压缩abc.tar.gz文件 tar -zxvf abc.tar.gz tar.bz2格式: 压

iOS开发中的压缩以及解压

事实上,在iOS开发中,压缩与解压,我都是采用第三方框架SSZipArchive实现的 gitHub地址:   https://github.com/ZipArchive/ZipArchive 上面有详细的使用方法 因为ZipArchive不支持ARC,所以如果你的工程开启了ARC,那么就需要对ZipArchive设置一下.在ZipArchive.mm编译选项中,增加-fno-objc-arc即可. 最后,需要为工程链接libz.dylib动态链接库. 使用示范(压缩): // 获得mainBu

【转】Hadoop在MapReduce中使用压缩详解

原文链接 http://www.cnblogs.com/ggjucheng/archive/2012/04/22/2465580.html#top hadoop对于压缩文件的支持 hadoop对于压缩格式的是透明识别,我们的MapReduce任务的执行是透明的,hadoop能够自动为我们 将压缩的文件解压,而不用我们去关心. 如果我们压缩的文件有相应压缩格式的扩展名(比如lzo,gz,bzip2等),hadoop就会根据扩展名去选择解码器解压. hadoop对每个压缩格式的支持,详细见下表: