今天来学习一下redis里面的整数集合。

typedef struct intset {  //结构体大小为8uint32_t encoding;  //编码方式uint32_t length;  //数组长度int8_t contents[];  //数组下表从0开始
} intset;

encoding是contents数组元素的编码方式,contents里面存储整数。

/** Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:**   * Redistributions of source code must retain the above copyright notice,*     this list of conditions and the following disclaimer.*   * Redistributions in binary form must reproduce the above copyright*     notice, this list of conditions and the following disclaimer in the*     documentation and/or other materials provided with the distribution.*   * Neither the name of Redis nor the names of its contributors may be used*     to endorse or promote products derived from this software without*     specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE* POSSIBILITY OF SUCH DAMAGE.*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "intset.h"
#include "zmalloc.h"
#include "endianconv.h"/* Note that these encodings are ordered, so:* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))  //2
#define INTSET_ENC_INT32 (sizeof(int32_t))  //4
#define INTSET_ENC_INT64 (sizeof(int64_t))  //8/* Return the required encoding for the provided value. */
static uint8_t _intsetValueEncoding(int64_t v) {//返回适用于传入值v的编码方式if (v < INT32_MIN || v > INT32_MAX)return INTSET_ENC_INT64;else if (v < INT16_MIN || v > INT16_MAX)return INTSET_ENC_INT32;elsereturn INTSET_ENC_INT16;
}/* Return the value at pos, given an encoding. */
//根据给定的编码方式enc,返回集合的底层数组在pos索引上的元素
static int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {int64_t v64;int32_t v32;int16_t v16;if (enc == INTSET_ENC_INT64) {memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));memrev64ifbe(&v64);return v64;} else if (enc == INTSET_ENC_INT32) {memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));memrev32ifbe(&v32);return v32;} else {memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));memrev16ifbe(&v16);return v16;}
}/* Return the value at pos, using the configured encoding. */
static int64_t _intsetGet(intset *is, int pos) {//根据集合的编码方式,返回底层数组在 pos 索引上的值return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));
}/* Set the value at pos, using the configured encoding. */
static void _intsetSet(intset *is, int pos, int64_t value) {//根据集合的编码方式,将底层数组在 pos 位置上的值设为 valueuint32_t encoding = intrev32ifbe(is->encoding);if (encoding == INTSET_ENC_INT64) {((int64_t*)is->contents)[pos] = value;memrev64ifbe(((int64_t*)is->contents)+pos);} else if (encoding == INTSET_ENC_INT32) {((int32_t*)is->contents)[pos] = value;memrev32ifbe(((int32_t*)is->contents)+pos);} else {((int16_t*)is->contents)[pos] = value;memrev16ifbe(((int16_t*)is->contents)+pos);}
}/* Create an empty intset. */
intset *intsetNew(void) {//创建并返回一个新的空整数集合intset *is = zmalloc(sizeof(intset));is->encoding = intrev32ifbe(INTSET_ENC_INT16); //存储字节序转换is->length = 0;return is;
}/* Resize the intset */
static intset *intsetResize(intset *is, uint32_t len) {//调整整数集合的内存空间大小uint32_t size = len*intrev32ifbe(is->encoding);is = zrealloc(is,sizeof(intset)+size);//sizeof(intset)是头需要存储的长度,size是数据的长度return is;
}/* Search for the position of "value". Return 1 when the value was found and* sets "pos" to the position of the value within the intset. Return 0 when* the value is not present in the intset and sets "pos" to the position* where "value" can be inserted. *///在集合is的底层数组中查找值value所在的索引;成功找到value时,函数返回1,并将*pos的值设为value所在的索引//当在数组中没找到 value 时,返回 0 。并将 *pos 的值设为 value 可以插入到数组中的位置。
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {//printf("---intsetSearch---%d,%d\n",value,*pos);int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;int64_t cur = -1;/* The value can never be found when the set is empty */if (intrev32ifbe(is->length) == 0) { //为空的情况if (pos) *pos = 0;return 0;} else {/* Check for the case where we know we cannot find the value,* but do know the insert position. */if (value > _intsetGet(is,max)) {  //和最后一个元素对比if (pos) *pos = intrev32ifbe(is->length);return 0;} else if (value < _intsetGet(is,0)) { //和第一个元素对比if (pos) *pos = 0;return 0;}}//二分查找while(max >= min) {mid = ((unsigned int)min + (unsigned int)max) >> 1;cur = _intsetGet(is,mid);  //获取值if (value > cur) {min = mid+1;} else if (value < cur) {max = mid-1;} else {break;}}if (value == cur) {if (pos) *pos = mid;return 1;} else {if (pos) *pos = min;return 0;}
}/* Upgrades the intset to a larger encoding and inserts the given integer. */
//根据值 value 所使用的编码方式,对整数集合的编码进行升级,并将值 value 添加到升级后的整数集合中
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {uint8_t curenc = intrev32ifbe(is->encoding);//字节序转换uint8_t newenc = _intsetValueEncoding(value); //新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;
}static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {//向前或先后移动指定索引范围内的数组元素void *src, *dst;uint32_t bytes = intrev32ifbe(is->length)-from; //要移动元素的个数uint32_t encoding = intrev32ifbe(is->encoding);if (encoding == INTSET_ENC_INT64) {src = (int64_t*)is->contents+from;dst = (int64_t*)is->contents+to;bytes *= sizeof(int64_t);} else if (encoding == INTSET_ENC_INT32) {src = (int32_t*)is->contents+from;dst = (int32_t*)is->contents+to;bytes *= sizeof(int32_t);} else {src = (int16_t*)is->contents+from;dst = (int16_t*)is->contents+to;bytes *= sizeof(int16_t);}memmove(dst,src,bytes);//memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中
}/* Insert an integer in the intset */
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {//尝试将元素 value 添加到整数集合中uint8_t valenc = _intsetValueEncoding(value);uint32_t pos;if (success) *success = 1; //success指示添加是否成功/* 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. */printf("encoding =%d\n",intrev32ifbe(is->encoding)); 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)) {  //成功找到value返回1,没找到返回0。并这只一个合适的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;
}/* Delete integer from intset */
intset *intsetRemove(intset *is, int64_t value, int *success) {//从整数集合中删除值valueuint8_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;
}/* Determine whether a value belongs to this set */
uint8_t intsetFind(intset *is, int64_t value) {//检查给定值 value 是否集合中的元素uint8_t valenc = _intsetValueEncoding(value);return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);
}/* Return random member */
int64_t intsetRandom(intset *is) {//从整数集合中随机返回一个元素return _intsetGet(is,rand()%intrev32ifbe(is->length));
}/* Get the value at the given position. When this position is* out of range the function returns 0, when in range it returns 1. */
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {//取出集合底层数组指定位置中的值,并将它保存到value指针中if (pos < intrev32ifbe(is->length)) {*value = _intsetGet(is,pos);return 1;}return 0;
}/* Return intset length */
uint32_t intsetLen(const intset *is) {//返回整数集合现有的元素个数return intrev32ifbe(is->length);
}/* Return intset blob size in bytes. */
size_t intsetBlobLen(intset *is) {//返回整数集合现在占用的字节总数量return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);
}

整数集合会根据整数大小选择合适的编码方式,很节约内存。

redis之intset相关推荐

  1. Redis之intset(整数集合)

    Redis中set数据结构它是由intset或者hashtable构成的今天我们就来讲一下intset 整数集合(intset)呢, 是一个有序的存储数据的结构 它有以下优点 1.整数集合中, 元素按 ...

  2. redis底层数据结构之intset

    最近,我想通过redis的源码来学习redis.虽然平时工作中用得不多,不过对redis还是比较感兴趣的,毕竟它的性能是不错的.redis是一个开源的项目,我们可以通过源代码去了解redis.我后面会 ...

  3. Redis系列四:redis支持的数据类型

    一.字符串<String> 1. 字符串类型:实际上可以是字符串(包括XML JSON),还有数字(整形 浮点数),二进制(图片 音频 视频),最大不能超过512MB 2. 设值命令: s ...

  4. 一文读懂Redis常见对象类型的底层数据结构

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 转自:伍 ...

  5. redis占用内存过低_使用多种数据结构优化Redis 内存占用

    背景 广告平台 adx 在处理曝光/点击上报时,使用 redis 的 setnx 命令去重,其逻辑如下 构造一个形如 s:track:%d:%s:%s 的 key,参数分别是上报类型(曝光 or 点击 ...

  6. Redis张工的set存储结构(实现)原理

    Redis 用intset 或hashtable 存储set.如果元素都是整数类型,就用inset 存储. 如果不是整数类型,就用hashtable(数组+链表的存来储结构). 问题:KV 怎么存储s ...

  7. 图解 Redis 五种数据结构底层实现

    Redis 是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis 支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集 ...

  8. redis获取byte数组_《Redis深度历险》读书笔记

    list/set/hash/zset 这四种数据结构是容器型数据结构: 1.create if not exists 2.drop if no elements 阻塞读在队列没有数据的时候,会立即进入 ...

  9. redis字符串匹配_Redis设计原理

    1.简介 Redis中的每个Key-Value在内存中都会被划分成DictEntry.RedisObject以及具体对象,其中DictEntry又分别包含指向Key和Value的指针(以RedisOb ...

最新文章

  1. python打包成exe可执行文件指定进程名字
  2. Kubernetes学习笔记二:Namespace,Cgroups 的隔离与应用
  3. 【Compiling Swift source files】编译很慢;
  4. 计算机组成原理怎么考察的,计算机组成原理课程考察报告(论文).doc
  5. AS导入第三方库方法和PullToRefresh的简单使用
  6. linux常用ogg脚本,ogg基本监控脚本
  7. VTK:结构化网格之BlankPoint
  8. python的生成器
  9. Android Launcher 分析
  10. HDU - 3551 Hard Problem(一般图最大匹配)
  11. PHP在Tomcat中CSS出错,tomcat找不到css怎么办
  12. 【转】ABP源码分析六:依赖注入的实现
  13. React 组件间传值的几种情形
  14. Dubbo 集成 ZooKeeper 注册中心实现服务调用
  15. Java 关键字super和this的使用及区别
  16. LPVOID lpParameter
  17. 海贼王java7723_我的世界海贼王模组7723首发版
  18. 怎样把两个表格合并成一个
  19. Java Runtime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Ecli
  20. NetApp Storage MetroCluster 双活解析

热门文章

  1. Codeforces Round #520 (Div. 2)
  2. opencv使用问题总结
  3. Kali Day01 --- arpspoof命令进行断网攻击(ARP欺骗)
  4. [JLOI 2011]飞行路线[USACO 09FEB]Revamping Trails
  5. 安装nginx+ngx_lua支持WAF防护功能
  6. session 对象的简单实例
  7. android ImageButton的图片怎么定义?
  8. 安装Adventure Works 2008 R2演示数据库
  9. Eclipse中使用git前账号密码等的配置问题
  10. JSON——Java中的使用