Redis源代码分析(二十四)--- tool工具类(2)

在上篇文章中初步的分析了一下,Redis工具类文件里的一些使用方法,包含2个随机算法和循环冗余校验算法,今天,继续学习Redis中的其它的一些辅助工具类的使用方法。包含里面的大小端转换算法,sha算法在Redis中的实现和通用工具类算法util.c。

先来看看大小端转换算法,大小端学习过操作系统的人一定知道是什么意思,在不同的操作系统中,高位数字的存储方式存在,高位在前,低位在后,或是高位在后,低位在前,所以这里面就涉及到转换,依据不同的操作系统,有不同的转换方式,所以Redis在这方面就开放了这样一批的API;

/* 对于16位,32位,64位作大小端的转换 */
void memrev16(void *p);
void memrev32(void *p);
void memrev64(void *p);
uint16_t intrev16(uint16_t v);
uint32_t intrev32(uint32_t v);
uint64_t intrev64(uint64_t v);

挑出当中的一个API的实现:

/* Toggle the 32 bit unsigned integer pointed by *p from little endian to
 * big endian */
/* 32位须要4个字节,第0和第3个,第1和第2个字节作交换 */
void memrev32(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[3];
    x[3] = t;
    t = x[1];
    x[1] = x[2];
    x[2] = t;
}

总之就是做头尾部的交换。

以下在Redis中的加密算法的实现,採用的是SHA算法,/SHA:Secure Hash Algorithm安全散列算法,与MD5算法类似,也是属于单向加密算法,在加密长度上,做了非常大的扩展,安全性也更高长度不超过2^64位的字符串或二进制流,经过SHA-1编码后,生成一个160位的二进制串 。在Redis中的C语言调用:

int
main(int argc, char **argv)
{
    SHA1_CTX ctx;
    unsigned char hash[20], buf[BUFSIZE];
    int i;

    for(i=0;i<BUFSIZE;i++)
        buf[i] = i;
	/* Redis代码中SHA算法的调用方法 */
    SHA1Init(&ctx);
    for(i=0;i<1000;i++)
        SHA1Update(&ctx, buf, BUFSIZE);
    SHA1Final(hash, &ctx);

    printf("SHA1=");
    for(i=0;i<20;i++)
        printf("%02x", hash[i]);
    printf("\n");
    return 0;
}

最后说说里面的util.c通用工具类的算法实现,里面但是有很多亮点的存在,先给出详细的API,主要涉及的是数字和字符串之间的转换:

int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase); /*支持glob-style的通配符格式,如*表示随意一个或多个字符,?表示随意字符,[abc]表示方括号里随意一个字母。*/
int stringmatch(const char *p, const char *s, int nocase); /*支持glob-style的通配符格式,长度的计算直接放在方法内部了,直接传入模式和原字符串*/
long long memtoll(const char *p, int *err); /* 内存大小转化为单位为字节大小的数值表示 */
int ll2string(char *s, size_t len, long long value); /* long long类型转化为string类型 */
int string2ll(const char *s, size_t slen, long long *value); /* String类型转换为long long类型 */
int string2l(const char *s, size_t slen, long *value); /* String类型转换为long类型,核心调用的方法还是string2ll()方法 */
int d2string(char *buf, size_t len, double value); /* double类型转化为String类型 */
sds getAbsolutePath(char *filename); /* 获取输入文件名称的绝对路径 */
int pathIsBaseName(char *path); /* 推断一个路径是否就是纯粹的文件名称,不是相对路径或是绝对路径 */

看第一个方法,正則表達式匹配的原理实现,平时我们仅仅知道去调用系统的正則表達式去匹配字符串,却不知道当中的原理,今天总是明确了:

/* Glob-style pattern matching. */
/*支持glob-style的通配符格式,如*表示随意一个或多个字符,?表示随意字符,[abc]表示方括号里随意一个字母。*/
int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':
            while (pattern[1] == '*') {
            	//假设出现的是**,说明一定匹配
                pattern++;
                patternLen--;
            }
            if (patternLen == 1)
                return 1; /* match */
            while(stringLen) {
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */
                string++;
                stringLen--;
            }
            return 0; /* no match */
            break;
        case '?':
            if (stringLen == 0)
                return 0; /* no match */
            /* 由于?能代表不论什么字符,所以,匹配的字符再往后挪一个字符 */
            string++;
            stringLen--;
            break;
        case '[':
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) {
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                	//假设遇到转义符,则模式字符往后移一个位置
                    pattern++;
                    patternLen--;
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') {
                	//直到遇到另外一个我中括号,则停止
                    break;
                } else if (patternLen == 0) {
                    pattern--;
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) {
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) {
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) {
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
            if (patternLen >= 2) {
                pattern++;
                patternLen--;
            }
            /* fall through */
        default:
        	/* 假设没有正則表達式的keyword符,则直接比較 */
            if (!nocase) {
                if (pattern[0] != string[0])
                	//不相等,直接不匹配
                    return 0; /* no match */
            } else {
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
            while(*pattern == '*') {
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    if (patternLen == 0 && stringLen == 0)
    	//假设匹配字符和模式字符匹配的长度都降低到0了,说明匹配成功了
        return 1;
    return 0;
}

很奇妙的代码吧,从来没有想过去实现正則表達式原理的代码。另一个方法是ll2string方法,数字转字符的方法,假设是我们寻常的做法,就是除10取余,加上相应的数字字符,但是要转换的但是ll类型啊,长度很长,效率会导致比較低,所以在Redis中作者,直接按除100算,2位,2位的赋值,并且用数字字符数字,做处理,直接按下标来赋值,避免了对余数的多次推断:

/* Convert a long long into a string. Returns the number of
 * characters needed to represent the number.
 * If the buffer is not big enough to store the string, 0 is returned.
 *
 * Based on the following article (that apparently does not provide a
 * novel approach but only publicizes an already used technique):
 *
 * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
 *
 * Modified in order to handle signed integers since the original code was
 * designed for unsigned integers. */
/* long long类型转化为string类型 */
int ll2string(char* dst, size_t dstlen, long long svalue) {
    static const char digits[201] =
        "0001020304050607080910111213141516171819"
        "2021222324252627282930313233343536373839"
        "4041424344454647484950515253545556575859"
        "6061626364656667686970717273747576777879"
        "8081828384858687888990919293949596979899";
    int negative;
    unsigned long long value;

    /* The main loop works with 64bit unsigned integers for simplicity, so
     * we convert the number here and remember if it is negative. */
    /* 在这里做正负号的推断处理 */
    if (svalue < 0) {
        if (svalue != LLONG_MIN) {
            value = -svalue;
        } else {
            value = ((unsigned long long) LLONG_MAX)+1;
        }
        negative = 1;
    } else {
        value = svalue;
        negative = 0;
    }

    /* Check length. */
    uint32_t const length = digits10(value)+negative;
    if (length >= dstlen) return 0;

    /* Null term. */
    uint32_t next = length;
    dst[next] = '\0';
    next--;
    while (value >= 100) {
    	//做值的换算
        int const i = (value % 100) * 2;
        value /= 100;
        //i所代表的余数值用digits字符数组中的相应数字取代了
        dst[next] = digits[i + 1];
        dst[next - 1] = digits[i];
        next -= 2;
    }

    /* Handle last 1-2 digits. */
    if (value < 10) {
        dst[next] = '0' + (uint32_t) value;
    } else {
        int i = (uint32_t) value * 2;
        dst[next] = digits[i + 1];
        dst[next - 1] = digits[i];
    }

    /* Add sign. */
    if (negative) dst[0] = '-';
    return length;
}

digit[201]就是从00-99的数字字符,余数的赋值就通过这个数组,高效,方便,是提高了非常多的速度。又发现了Redis代码中的一些亮点。

时间: 2024-10-07 21:43:23

Redis源代码分析(二十四)--- tool工具类(2)的相关文章

Java从零开始学二十四点(集合工具类Collections)

一.Collections简介 在集合的应用开发中,集合的若干接口和若干个子类是最最常使用的,但是在JDK中提供了一种集合操作的工具类 —— Collections,可以直接通过此类方便的操作集合 二.Collections类的常用方法及常量 No. 方法 类型 描述 1 public static final List EMPTY_LIST 常量 返回一个空的List集合 2 public static final Set EMPTY_SET 常量 返回空的Set集合 3 public sta

Redis源码分析(二十四)--- tool工具类(2)

在上篇文章中初步的分析了一下,Redis工具类文件中的一些用法,包括2个随机算法和循环冗余校验算法,今天,继续学习Redis中的其他的一些辅助工具类的用法.包括里面的大小端转换算法,sha算法在Redis中的实现和通用工具类算法util.c. 先来看看大小端转换算法,大小端学习过操作系统的人一定知道是什么意思,在不同的操作系统中,高位数字的存储方式存在,高位在前,低位在后,或是高位在后,低位在前,所以这里面就涉及到转换,根据不同的操作系统,有不同的转换方式,所以Redis在这方面就开放了这样一批

云计算设计模式(二十四)——仆人键模式

云计算设计模式(二十四)——仆人键模式 使用一个令牌或密钥,向客户提供受限制的直接访问特定的资源或服务,以便由应用程序代码卸载数据传输操作.这个模式是在使用云托管的存储系统或队列的应用中特别有用,并且可以最大限度地降低成本,最大限度地提高可扩展性和性能. 背景和问题 客户端程序和网络浏览器经常需要读取和写入文件或数据流,并从一个应用程序的存储空间.通常,应用程序将处理的运动数据,或者通过从存储读取它,并将其传输到客户端,或通过从客户机读取该载流并将其存储在数据存储中.然而,这种方法吸收了宝贵的资

Android学习路线(二十四)ActionBar Fragment运用最佳实践

通过前面的几篇博客,大家看到了Google是如何解释action bar和fragment以及推荐的用法.俗话说没有demo的博客不是好博客,下面我会介绍一下action bar和fragment在实战中的应用,以及相关demo源码,希望和大家相互交流. 了解过fragment的同学们应该都知道,fragment是android 3.0版本才出现的的,因此如果要在支持android 3.0一下版本的工程中使用fragment的话是需要添加Support Library的.具体如何添加我就不再赘述

QT开发(二十四)——QT文件操作

QT开发(二十四)--QT文件操作 一.QT文件操作简介 QT中的IO操作通过统一的接口简化了文件与外部设备的操作方式,QT中文件被当作一种特殊的外部设备,文件操作与外部设备操作相同. 1.IO操作的主要函数接口 打开设备:bool open(OpenMode mode) 读取数据:QByteArray read(qint64 maxSize) 写入数据:qint64 write(const QByteArray & byteArray) 关闭设备:void close() IO操作的本质是连续

请对照这二十四条逻辑谬误自行打脸(转自知乎谢熊猫专栏)

[科普工具文]请对照这二十四条逻辑谬误自行打脸 谢熊猫君 · 1 年前 两年前,我还活跃在人人网的时候,曾经整理过一篇常见逻辑谬误的工具文,用来帮助大家在网络讨论中打脸用: [科普工具文]请对照这二十四条逻辑谬误自行打脸 这两年来在一些社交网站和互联网论坛中常看到有人使用这篇文章,想来也是有点用处的,特意在这边转帖一下,为方便各位在知乎讨论时能更加方便的辨别逻辑问题. ======================= ======================= 本文内容基本都来自于英文网站ht

JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习

JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了.如果你初学,不懂IO流,你可以从前往后慢慢看,但是你工作了一段时间你会发现,流的使用场景以及技术点是非常的强硬的,我们势必要掌握这个知识点,如果你觉得翻阅API比较鼓噪,看视频得不到精髓,看书看不到要点,你就跟随我的JAVA之旅,一起去探索吧! 一.I/O概述 I/O全名:Input Output

Bootstrap&lt;基础二十四&gt; 缩略图

Bootstrap 缩略图.大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap 创建缩略图的步骤如下: 在图像周围添加带有 class .thumbnail 的 <a> 标签. 这会添加四个像素的内边距(padding)和一个灰色的边框. 当鼠标悬停在图像上时,会动画显示出图像的轮廓. 下面的实例演示了默认的缩略图: <!DOCTYPE html> <html> <head> &l

C++语言学习(十四)——C++类成员函数调用分析

C++语言学习(十四)--C++类成员函数调用分析 一.C++成员函数 1.C++成员函数的编译 C++中的函数在编译时会根据命名空间.类.参数签名等信息进行重新命名,形成新的函数名.函数重命名的过程通过一个特殊的Name Mangling(名字编码)算法来实现.Name Mangling算法是一种可逆的算法,既可以通过现有函数名计算出新函数名,也可以通过新函数名逆向推导出原有函数名.Name Mangling算法可以确保新函数名的唯一性,只要命名空间.所属的类.参数签名等有一个不同,那么产生的