内容简介:今天来讲讲redis当中set的一种实现形式intset,顾名思义,其应用场景只是在集合当中只包含整数值并且元素数量不多时,set才会采用的一种实现方式。其存储结构如下所示:其中三个宏定义表示的是encoding字段的可能取值,分别对应intset中的contents数组的三种不同的解析方式(16位整数,32位整数,64位整数)。
今天来讲讲 redis 当中set的一种实现形式intset,顾名思义,其应用场景只是在集合当中只包含整数值并且元素数量不多时,set才会采用的一种实现方式。
其存储结构如下所示:
点击( 此处 )折叠或打开
- typedef struct intset {
- uint32_t encoding ; / / 编码规则,具体见下面三个宏定义
- uint32_t length ; / / 所存元素个数
- int8_t contents [ ] ; / / 元素存储位置
- } intset ;
- #define INTSET_ENC_INT16 ( sizeof ( int16_t ) )
- #define INTSET_ENC_INT32 ( sizeof ( int32_t ) )
- #define INTSET_ENC_INT64 ( sizeof ( int64_t ) )
其中三个宏定义表示的是encoding字段的可能取值,分别对应intset中的contents数组的三种不同的解析方式(16位整数,32位整数,64位整数)。 这里需要注意的是由于不同的机器本地 的endian可能 不同,redis使用了endianconv.h文件中的函数对这方面进行了统一。 在contents中存放的实际数字时按照从小到大的顺序排列好的,并且没有重复项。
整个intset里面比较好玩的就是插入元素了。由于有三种不同的编码方式,当插入的新元素所占的空间较大,大到目前intset自身的编码无法承载这一数据的时候,intset就会根据新插入的值得大小,将原有的inset的encoding变为需要的,其具体步骤如下:
- 根据插入的元素类型,适当扩展intset所占的空间
- 在维持低层数组存储数据不变的基础上,按照 从后到前的顺序(避免覆盖) 拷贝到新的位置上
- 将新添加的元素插入到intset当中
其中插入代码如下所示:
点击( 此处 )折叠或打开
- / * Insert an integer in the intset * /
- intset * intsetAdd ( intset * is , int64_t value , uint8_t * success ) {
- uint8_t valenc = _intsetValueEncoding ( value ) ;
- uint32_t pos ;
- if ( success ) * success = 1 ;
- / * Upgrade encoding if necessary . If we need to upgrade , we know that
- * this value should be either appended ( if > 0 ) or prepended ( if < 0 ) ,
- * because it lies outside the range of existing values . * /
- if ( valenc > intrev32ifbe ( is - > encoding ) ) {
- / * This always succeeds , so we don ' t need to curry * success . * /
- return intsetUpgradeAndAdd ( is , value ) ;
- } else {
- / * Abort if the value is already present in the set .
- * This call will populate "pos" with the right position to insert
- * the value when it cannot be found . * /
- if ( intsetSearch ( is , value , & pos ) ) {
- if ( success ) * success = 0 ;
- return is ;
- }
- is = intsetResize ( is , intrev32ifbe ( is - > length ) + 1 ) ;
- if ( pos < intrev32ifbe ( is - > length ) ) intsetMoveTail ( is , pos , pos + 1 ) ;
- }
- _intsetSet ( is , pos , value ) ;
- is - > length = intrev32ifbe ( intrev32ifbe ( is - > length ) + 1 ) ;
- return is ;
- }
在这个函数当中,首先判断了插入值的编码,若应该分配较大的空间,直接调用update函数
点击( 此处 )折叠或打开
- / * Upgrades the intset to a larger encoding and inserts the given integer . * /
- static intset * intsetUpgradeAndAdd ( intset * is , int64_t value ) {
- uint8_t curenc = intrev32ifbe ( is - > encoding ) ;
- uint8_t newenc = _intsetValueEncoding ( value ) ;
- int length = intrev32ifbe ( is - > length ) ;
- int prepend = value < 0 ? 1 : 0 ;
- / * First set new encoding and resize * /
- is - > encoding = intrev32ifbe ( newenc ) ;
- is = intsetResize ( is , intrev32ifbe ( is - > length ) + 1 ) ;
- / * Upgrade back - to - front so we don ' t overwrite values .
- * Note that the "prepend" variable is used to make sure we have an empty
- * space at either the beginning or the end of the intset .
- * 通俗的将就是对新插入的元素,我们应该插在头部还是插在尾部 * /
- while ( length - - )
- _intsetSet ( is , length + prepend , _intsetGetEncoded ( is , length , curenc ) ) ;
- / * Set the value at the beginning or the end . * /
- if ( prepend )
- _intsetSet ( is , 0 , value ) ;
- else
- _intsetSet ( is , intrev32ifbe ( is - > length ) , value ) ;
- is - > length = intrev32ifbe ( intrev32ifbe ( is - > length ) + 1 ) ;
- return is ;
- }
如果当前intset当中已经有该元素的话,直接返回。若没有,需要找到应该插入的位置,移动其后面的元素,再将该值插入到应该插入的位置。
删除操作的话,逻辑就是能否找到,若找到的话,就直接将后面的元素移动过来,直接覆盖即可
点击( 此处 )折叠或打开
- / * Delete integer from intset * /
- intset * intsetRemove ( intset * is , int64_t value , int * success ) {
- uint8_t valenc = _intsetValueEncoding ( value ) ;
- uint32_t pos ;
- if ( success ) * success = 0 ;
- if ( valenc < = intrev32ifbe ( is - > encoding ) & & intsetSearch ( is , value , & pos ) ) {
- uint32_t len = intrev32ifbe ( is - > length ) ;
- / * We know we can delete * /
- if ( success ) * success = 1 ;
- / * Overwrite value with tail and update length * /
- if ( pos < ( len - 1 ) ) intsetMoveTail ( is , pos + 1 , pos ) ;
- is = intsetResize ( is , len - 1 ) ;
- is - > length = intrev32ifbe ( len - 1 ) ;
- }
- return is ;
- }
这里面需要提醒诸位的是:
删除元素的时候没有降级操作!!!个人猜测的原因为:如果删除一个元素就要从头到尾检查当前的encoding是否过大的话,就会造成不必要的消耗,还不如让其多占用点内存了~~~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- 【源码阅读】Gson源码阅读
- 如何阅读Java源码 ,阅读java的真实体会
- 我的源码阅读之路:redux源码剖析
- JDK源码阅读(六):HashMap源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。