浅析Open vSwitch数据结构:哈希表hmap/smap/shash

文章目录

    • 概述
    • hmap
      • hmap数据结构
      • 初始化hmap
      • 插入节点
      • 扩展hmap空间
        • resize函数
      • 删除节点
      • 遍历所有节点
        • 辅助函数hmap_first
        • 辅助函数hmap_next
    • smap
      • smap数据结构
      • 插入节点
      • 删除节点
      • 查找节点
      • 遍历所有节点
    • shash
      • shash数据结构
      • 插入节点
      • 删除节点
      • 查找节点
      • 遍历所有节点

概述

在OVS软件中,hmap提供了基础的哈希表存储结构,smap和shash基于hmap进行实现,其中smap支持存储字符串,而sshash则支持存储任意类型的数据。

hmap

OVS软件的hmap是基于分离链接法实现的,分离链接法使用链表解决散列冲突,其做法是散列值相同的元素都保存到一个链表中。当查询的时候,首先根据散列值找到元素所在的链表,然后遍历链表查找对应的元素。
在这里插入图片描述

hmap数据结构

struct hmap {struct hmap_node **buckets;     // 哈希数组,本质上是一个分离链表数组,当mask=0时,指向成员one的地址struct hmap_node *one;      // 仅在mask为0时使用size_t mask;    // 哈希桶的大小size_t n;   // 哈希表中存储的hmap_node节点数量
};// 哈希节点
struct hmap_node {size_t hash;    // 哈希值struct hmap_node *next;    // 单向链表,所有哈希值相同的节点会链接到一个链表中
};

初始化hmap

void hmap_init(struct hmap *hmap)
{hmap->buckets = &hmap->one;hmap->one = NULL;hmap->mask = 0;hmap->n = 0;
}

插入节点

#define hmap_insert(HMAP, NODE, HASH) \hmap_insert_at(HMAP, NODE, HASH, OVS_SOURCE_LOCATOR)static inline void
hmap_insert_at(struct hmap *hmap, struct hmap_node *node, size_t hash,const char *where)
{hmap_insert_fast(hmap, node, hash);     // 执行快速插入if (hmap->n / 2 > hmap->mask) {     // 当存储节点数量的一半大于mask时,需要对hmap进行扩容hmap_expand_at(hmap, where);}
}static inline void
hmap_insert_fast(struct hmap *hmap, struct hmap_node *node, size_t hash)
{struct hmap_node **bucket = &hmap->buckets[hash & hmap->mask];      // 计算hash值,并根据hash值确定数组索引node->hash = hash;node->next = *bucket;*bucket = node;     // 新节点插入到链表头部hmap->n++;      // 更新hmap存储节点数量
}

扩展hmap空间

void
hmap_expand_at(struct hmap *hmap, const char *where)
{size_t new_mask = calc_mask(hmap->n);       // 根据已存储节点的数量计算出新的mask值if (new_mask > hmap->mask) {COVERAGE_INC(hmap_expand);resize(hmap, new_mask, where);}
}static size_t
calc_mask(size_t capacity)
{size_t mask = capacity / 2;mask |= mask >> 1;mask |= mask >> 2;mask |= mask >> 4;mask |= mask >> 8;mask |= mask >> 16;
#if SIZE_MAX > UINT32_MAXmask |= mask >> 32;
#endifmask |= (mask & 1) << 1;return mask;
}

resize函数

static void
resize(struct hmap *hmap, size_t new_mask, const char *where)
{struct hmap tmp;size_t i;ovs_assert(is_pow2(new_mask + 1));hmap_init(&tmp);        // 初始化临时的hmapif (new_mask) {tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));    // 根据new_mask创建哈希数组tmp.mask = new_mask;for (i = 0; i <= tmp.mask; i++) {tmp.buckets[i] = NULL;}}int n_big_buckets = 0;int biggest_count = 0;int n_biggest_buckets = 0;for (i = 0; i <= hmap->mask; i++) {struct hmap_node *node, *next;int count = 0;for (node = hmap->buckets[i]; node; node = next) {next = node->next;hmap_insert_fast(&tmp, node, node->hash);       // 将原hmap的元素插入到临时hmap中count++;}}hmap_swap(hmap, &tmp);  // 交换两个hmap的结构,此时hmap的数据与原tmp是相同的hmap_destroy(&tmp);
}

删除节点

static inline void
hmap_remove(struct hmap *hmap, struct hmap_node *node)
{struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask];while (*bucket != node) {bucket = &(*bucket)->next;}*bucket = node->next;hmap->n--;
}

遍历所有节点

// 普通版本的遍历,不支持删除节点
#define HMAP_FOR_EACH(NODE, MEMBER, HMAP) \HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, (void) 0)#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...)                     \for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \(NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \|| ((NODE = NULL), false);                                     \ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER))// 安全版本的遍历,支持删除节点
#define HMAP_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HMAP) \HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, (void) 0)#define HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, ...)          \for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \((NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))               \|| ((NODE = NULL), false)                                     \? INIT_CONTAINER(NEXT, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), 1 \: 0);                                                         \(NODE) = (NEXT))

辅助函数hmap_first

static inline struct hmap_node *
hmap_next__(const struct hmap *hmap, size_t start)
{size_t i;for (i = start; i <= hmap->mask; i++) {struct hmap_node *node = hmap->buckets[i];if (node) {return node;}}return NULL;
}static inline struct hmap_node *
hmap_first(const struct hmap *hmap)
{return hmap_next__(hmap, 0);
}

辅助函数hmap_next

static inline struct hmap_node *
hmap_next(const struct hmap *hmap, const struct hmap_node *node)
{return (node->next? node->next        // 下一节点非空,返回下一节点: hmap_next__(hmap, (node->hash & hmap->mask) + 1));    
}

smap

smap基于hmap实现,是一个专用于存储key-value的哈希表,其中key和value类型都是字符串。

smap数据结构

struct smap {struct hmap map; 
};struct smap_node {struct hmap_node node;  char *key;char *value;
};

插入节点

struct smap_node *
smap_add(struct smap *smap, const char *key, const char *value)
{size_t key_len = strlen(key);return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),hash_bytes(key, key_len, 0));     // 为key和value申请内存空间
}static struct smap_node *
smap_add__(struct smap *smap, char *key, void *value, size_t hash)
{struct smap_node *node = xmalloc(sizeof *node);node->key = key;node->value = value;hmap_insert(&smap->map, &node->node, hash);     // 调用hmap接口插入节点return node;
}

删除节点

void
smap_remove_node(struct smap *smap, struct smap_node *node)
{hmap_remove(&smap->map, &node->node);free(node->key);free(node->value);free(node);
}void
smap_remove(struct smap *smap, const char *key)
{struct smap_node *node = smap_get_node(smap, key);if (node) {smap_remove_node(smap, node);}
}

查找节点

struct smap_node *
smap_get_node(const struct smap *smap, const char *key)
{size_t key_len = strlen(key);return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
}static struct smap_node *
smap_find__(const struct smap *smap, const char *key, size_t key_len,size_t hash)
{struct smap_node *node;HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {return node;}}return NULL;
}

遍历所有节点

// 普通版本的遍历,不支持删除,调用hmap的接口
#define SMAP_FOR_EACH(SMAP_NODE, SMAP)                                  \HMAP_FOR_EACH_INIT (SMAP_NODE, node, &(SMAP)->map,                  \BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *), \BUILD_ASSERT_TYPE(SMAP, struct smap *))// 安全版本的遍历,支持删除,调用hmap的接口
#define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP)           \HMAP_FOR_EACH_SAFE_INIT (                               \SMAP_NODE, NEXT, node, &(SMAP)->map,                \BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *),   \BUILD_ASSERT_TYPE(NEXT, struct smap_node *),        \BUILD_ASSERT_TYPE(SMAP, struct smap *))

shash

shash也是基于hmap进行扩展,支持存储任意类型的数据。

shash数据结构

struct shash_node {struct hmap_node node;char *name;void *data;
};struct shash {struct hmap map;
};

插入节点

static struct shash_node *
shash_add_nocopy__(struct shash *sh, char *name, const void *data, size_t hash)
{struct shash_node *node = xmalloc(sizeof *node);node->name = name;node->data = CONST_CAST(void *, data);      // 去除const属性进行存储hmap_insert(&sh->map, &node->node, hash);return node;
}struct shash_node *
shash_add_nocopy(struct shash *sh, char *name, const void *data)
{return shash_add_nocopy__(sh, name, data, hash_name(name));
}struct shash_node *
shash_add(struct shash *sh, const char *name, const void *data)
{return shash_add_nocopy(sh, xstrdup(name), data);       // 为key申请内存空间
}

删除节点

void
shash_delete(struct shash *sh, struct shash_node *node)
{free(shash_steal(sh, node));
}char *
shash_steal(struct shash *sh, struct shash_node *node)
{char *name = node->name;hmap_remove(&sh->map, &node->node);free(node);return name;
}

查找节点

static struct shash_node *
shash_find__(const struct shash *sh, const char *name, size_t name_len,size_t hash)
{struct shash_node *node;HMAP_FOR_EACH_WITH_HASH (node, node, hash, &sh->map) {if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {return node;}}return NULL;
}struct shash_node *
shash_find(const struct shash *sh, const char *name)
{return shash_find__(sh, name, strlen(name), hash_name(name));
}

遍历所有节点

#define SHASH_FOR_EACH(SHASH_NODE, SHASH)                               \HMAP_FOR_EACH_INIT (SHASH_NODE, node, &(SHASH)->map,                \BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \BUILD_ASSERT_TYPE(SHASH, struct shash *))#define SHASH_FOR_EACH_SAFE(SHASH_NODE, NEXT, SHASH)        \HMAP_FOR_EACH_SAFE_INIT (                               \SHASH_NODE, NEXT, node, &(SHASH)->map,              \BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \BUILD_ASSERT_TYPE(NEXT, struct shash_node *),       \BUILD_ASSERT_TYPE(SHASH, struct shash *))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/129722.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络安全带你练爬虫-100练】第23练:文件内容的删除+写入

目录 0x00 前言&#xff1a; 0x02 解决&#xff1a; 0x00 前言&#xff1a; 本篇博文可能会有一点点的超级呆 0x02 解决&#xff1a; 你是不是也会想&#xff1a; 使用pyrhon将指定文件夹位置里面的1.txt中数据全部删除以后---->然后再将参数req_text的值写入到1.txt …

rsa加密解密java和C#互通

前言 因为第三方项目是java的案例&#xff0c;但是原来的项目使用的是java&#xff0c;故需要将java代码转化为C#代码&#xff0c;其中核心代码就是RSA加密以及加签和验签&#xff0c;其他的都是api接口请求难度不大。 遇到的问题 java和c#密钥格式不一致&#xff0c;java使…

LeetCode(力扣)491. 递增子序列Python

LeetCode491. 递增子序列 题目链接代码 题目链接 https://leetcode.cn/problems/non-decreasing-subsequences/ 代码 class Solution:def backtracking(self, nums, index, result, path):if len(path) > 1:result.append(path[:])uset set()for i in range(index, len…

分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测

分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测 目录 分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于局部费歇尔判别数据降维的L…

日常开发小汇总(5)元素跟随鼠标移动(在视口下移动)

<div class"mark"><h1>title</h1><div><p>title 鼠标移动 盒子整体移动</p><p>test</p><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Modi, porro.</p></div></div>cons…

springboot MongoDB 主从 多数据源

上一篇&#xff0c;我写了关于用一个map管理mongodb多个数据源&#xff08;每个数据源&#xff0c;只有单例&#xff09;的内容。 springboot mongodb 配置多数据源 临到部署到阿里云的测试环境&#xff0c;发现还需要考虑一下主从的问题&#xff0c;阿里云买的数据库&#x…

入门力扣自学笔记279 C++ (题目编号:1123)

1123. 最深叶节点的最近公共祖先 题目&#xff1a; 给你一个有根节点 root 的二叉树&#xff0c;返回它 最深的叶节点的最近公共祖先 。 回想一下&#xff1a; 叶节点 是二叉树中没有子节点的节点树的根节点的 深度 为 0&#xff0c;如果某一节点的深度为 d&#xff0c;那它…

【Flutter】引入网络图片时,提示:Failed host lookup: ‘[图片host]‘

在使用 NetworkImage 组件加载外部图片时&#xff0c;提示 Failed host lookup: [图片host] 错误。 排查方向 1、清理缓存 解决方案&#xff1a; 尝试flutter clean清空缓存后重新安装依赖flutter pub get重新构建项目flutter create . 走完上述三个步骤后&#xff0c;再次…

MySql学习笔记11——DBA命令介绍

DBA命令 数据导入 要进入Mysql 创建数据库 create database database_name;使用数据库 use database_name;初始化数据库 source .sql文件地址&#xff0c;不能加双引号&#xff1b;数据导出 要在windows的dos环境下进行 导出数据库 mysqldump database_name > 存放…

Android 文字转语音播放实现

1&#xff0c;TextToSpeech类是android自带的&#xff0c;但是部分设备需要支持TTS需要增加语音库&#xff0c;我使用的是讯飞语音&#xff08;离线的哦&#xff09;。请自行下载并安装讯飞语音APK&#xff0c;然后到系统设置中设置TTS功能默认使用该选项。有自带TTS库的可以省…

《存储IO路径》专题:块设备层多队列blk-mq架构

我们想象一下&#xff0c;你是一个餐厅的厨师&#xff0c;你要准备很多不同的菜肴&#xff0c;而每种菜肴需要不同的食材和烹饪时间。如果每道菜都按照需要的顺序来准备&#xff0c;那么你的工作效率一定会非常低。为了提高效率&#xff0c;你会怎么做呢&#xff1f; 在linux架…

基于SSM的家政服务网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

windows 下载安装 mysql

windows 下载安装 mysql 官网地址&#xff1a;https://dev.mysql.com/ 下载地址&#xff1a;https://cdn.mysql.com//Downloads/MySQLInstaller/mysql-installer-community-8.0.34.0.msi 点击 Downloads 点击 MySQL Community (GPL) Downloads 点击 MySQL Installer for Window…

usb学习笔记

框架 usb 驱动是基于usb core 的&#xff0c;设备插上之后&#xff0c;host 层自然会进行识别&#xff0c;设备驱动通过core层的接口操作设备&#xff0c;而不用直接面对usb硬件。对于应用层需要封装成一个usb 的设备。 驱动是基于urb 数据进行操作的。 49 static void usb_mo…

Windows环境下Springboot3+Graalvm+Idea 打包成原生镜像 踩坑

https://github.com/oracle/graal/https://github.com/graalvm/graalvm-ce-builds/releases/对应关系graalvm-ce-java17-windows-amd64-X.X.X.zipnative-image-installable-svm-java17-windows-amd64-X.X.X.jar本人使用:graalvm-ce-java17-windows-amd64-23.0.1.zipnative-imag…

第4章_瑞萨MCU零基础入门系列教程之瑞萨 MCU 源码设计规范

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

【C++11】{}初始化、std::initializer_list、decltype、STL新增容器

文章目录 1. C11简介2. 统一的列表初始化2.1 &#xff5b;&#xff5d;初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype 4. nullptr5. 范围for循环6. 智能指针7. C11STL中的一些变化8. 演示代码 1. C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1…

英语单词(1)

1.void:空的 2.main:主要的 3.class:类 4.system:系统 5.out: 输出 6.print:打印 7.public:公共的,公用的 8.static:静态的,静止的 9.oracle:甲骨文公司 10.eclipse: java编程语言

生存游戏手游推荐,适合长期玩的生存类手游

今天小编为大家带来了生存游戏手游推荐&#xff0c;适合长期玩的生存类手游。许多朋友现在喜欢冒险&#xff0c;想体验荒野生活&#xff0c;但在现实中&#xff0c;由于各种原因可能实现不了。游戏中的生存可以满足玩家对狂野生存的幻想&#xff0c;让现实中未实现的梦想在虚拟…

element-ui switch开关组件二次封装,添加loading效果,点击时调用接口后改变状态

先看效果&#xff1a; element-ui中的switch开关无loading属性&#xff08;在element-plus时加入了&#xff09;&#xff0c;而且点击时开关状态就会切换&#xff0c;这使得在需要调用接口后再改变开关状态变得比较麻烦。 思路&#xff1a;switch开关外包一层div&#xff0c;给…