redis之intset
生活随笔
收集整理的這篇文章主要介紹了
redis之intset
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? ? ? ?今天來學習一下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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: char和unsigned char
- 下一篇: redis之zskiplist