结合redis设计与实现的redis源码学习-26-工具函数(Util.h/.c)
2017-12-18 23:48
781 查看
Redis将很多的公用转换函数独立了出来,放入了Util.h中,包括字符串对比,内存转换,字符串数字转换,获取路径等,Redis的作者都是自己实现的,在这里我将这些函数认真学习,观察是在哪里高效并可以在之后的工作中使用。
因为Util的函数都是完全独立逻辑的,所以我在这里只看.c文件
Util.c
ll2string
string2ll
getrandomhexchar
getabsolutepath
因为Util的函数都是完全独立逻辑的,所以我在这里只看.c文件
Util.c
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <limits.h> #include <math.h> #include <unistd.h> #include <sys/time.h> #include <float.h> #include <stdint.h> #include <errno.h> #include "sha1.h"//Sha1校验算法的实现文件 /* Glob-style pattern matching. 全局风格的模式对比*/ int stringmatchlen(const char *pattern, int patternLen, const char *string, int stringLen, 4000 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 这里递归自己来判断后面的字符,666*/ string++;//字符串的下一位 stringLen--; } return 0; /* no match 如果到这里了,就没有匹配到,因为如果匹配的话前面和下面已经都匹配了*/ break; case '?': if (stringLen == 0) return 0; /* no match 如果字符串长度是0,那么就不匹配了,因为模式是?*/ string++;否则到string的下一个 stringLen--; break; case '[': { int not, match; pattern++;//模式的下一位 patternLen--; not = pattern[0] == '^';//这里用int来表示bool值,因为在C里没有bool 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: 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) return 1; return 0; } int stringmatch(const char *pattern, const char *string, int nocase) { return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase); } /* Convert a string representing an amount of memory into the number of bytes, so for instance memtoll("1Gb") will return 1073741824 that is (1024*1024*1024).转换一个字符串表示的内存到数字 On parsing error, if *err is not NULL, it's set to 1, otherwise it's set to 0. On error the function return value is 0, regardless of the fact 'err' is NULL or not. */ //这里是针对redis自己表示内存的方式来转换的, long long memtoll(const char *p, int *err) { const char *u; char buf[128]; long mul; /* unit multiplier */ long long val; unsigned int digits; if (err) *err = 0; /* Search the first non digit character. 找到第一个非数字的符号*/ u = p; if (*u == '-') u++; while(*u && isdigit(*u)) u++; if (*u == '\0' || !strcasecmp(u,"b")) { mul = 1; } else if (!strcasecmp(u,"k")) { mul = 1000; } else if (!strcasecmp(u,"kb")) { mul = 1024; } else if (!strcasecmp(u,"m")) { mul = 1000*1000; } else if (!strcasecmp(u,"mb")) { mul = 1024*1024; } else if (!strcasecmp(u,"g")) { mul = 1000L*1000*1000; } else if (!strcasecmp(u,"gb")) { mul = 1024L*1024*1024; } else { if (err) *err = 1; return 0; } /* Copy the digits into a buffer, we'll use strtoll() to convert the digit (without the unit) into a number. 将数字的字符复制到一个buffer中*/ digits = u-p; if (digits >= sizeof(buf)) { if (err) *err = 1; return 0; } memcpy(buf,p,digits); buf[digits] = '\0'; char *endptr; errno = 0; val = strtoll(buf,&endptr,10); if ((val == 0 && errno == EINVAL) || *endptr != '\0') { if (err) *err = 1; return 0; } return val*mul; } /* Return the number of digits of 'v' when converted to string in radix 10. See ll2string() for more information. 当以10进制转换为字符串时返回v*/ uint32_t digits10(uint64_t v) { if (v < 10) return 1; if (v < 100) return 2; if (v < 1000) return 3; if (v < 1000000000000UL) { if (v < 100000000UL) { if (v < 1000000) { if (v < 10000) return 4; return 5 + (v >= 100000); } return 7 + (v >= 10000000UL); } if (v < 10000000000UL) { return 9 + (v >= 1000000000UL); } return 11 + (v >= 100000000000UL); } return 12 + digits10(v / 1000000000000UL); } /* Like digits10() but for signed values. 类似于上面的函数但是支队标识值*/ uint32_t sdigits10(int64_t v) { if (v < 0) { /* Abs value of LLONG_MIN requires special handling. */ uint64_t uv = (v != LLONG_MIN) ? (uint64_t)-v : ((uint64_t) LLONG_MAX)+1; return digits10(uv)+1; /* +1 for the minus. */ } else { return digits10(v); } }
ll2string
/* 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.将长整型转换为一个字符串,返回表示使用的字符串,如果缓冲区不足,返回0 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. */ 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. 为了简单起见,主循环与64位无符号整数一起使用,所以我们在这里转换数字并记住它是否为负数*/ 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; 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; }
string2ll
/* Convert a string into a long long. Returns 1 if the string could be parsed * into a (non-overflowing) long long, 0 otherwise. The value will be set to * the parsed value when appropriate. */ int string2ll(const char *s, size_t slen, long long *value) { const char *p = s; size_t plen = 0; int negative = 0; unsigned long long v; if (plen == slen) return 0; /* Special case: first and only digit is 0. */ if (slen == 1 && p[0] == '0') { if (value != NULL) *value = 0; return 1; } if (p[0] == '-') { negative = 1; p++; plen++; /* Abort on only a negative sign. */ if (plen == slen) return 0; } /* First digit should be 1-9, otherwise the string should just be 0. */ if (p[0] >= '1' && p[0] <= '9') { v = p[0]-'0'; p++; plen++; } else if (p[0] == '0' && slen == 1) { *value = 0; return 1; } else { return 0; } while (plen < slen && p[0] >= '0' && p[0] <= '9') { if (v > (ULLONG_MAX / 10)) /* Overflow. */ return 0; v *= 10; if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ return 0; v += p[0]-'0'; p++; plen++; } /* Return if not all bytes were used. */ if (plen < slen) return 0; if (negative) { if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ return 0; if (value != NULL) *value = -v; } else { if (v > LLONG_MAX) /* Overflow. */ return 0; if (value != NULL) *value = v; } return 1; }
getrandomhexchar
/* Generate the Redis "Run ID", a SHA1-sized random number that identifies a given execution of Redis, so that if you are talking with an instance having run_id == A, and you reconnect and it has run_id == B, you can be sure that it is either a different instance or it was restarted. 生成redis运行id,这是一个sha1大小的随机数,用于标识给定的Redis执行情况*/ void getRandomHexChars(char *p, unsigned int len) { char *charset = "0123456789abcdef"; unsigned int j; /* Global state. */ static int seed_initialized = 0; static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */ static uint64_t counter = 0; /* The counter we hash with the seed. */ if (!seed_initialized) { /* Initialize a seed and use SHA1 in counter mode, where we hash the same seed with a progressive counter. For the goals of this function we just need non-colliding strings, there are no cryptographic security needs. 在计数器模式下初始化一个种子并使用SHA1,在那里我们用一个累进计数器对相同的种子进行哈希处理*/ FILE *fp = fopen("/dev/urandom","r"); if (fp && fread(seed,sizeof(seed),1,fp) == 1) seed_initialized = 1; if (fp) fclose(fp); } if (seed_initialized) { while(len) { unsigned char digest[20]; SHA1_CTX ctx; unsigned int copylen = len > 20 ? 20 : len; SHA1Init(&ctx); SHA1Update(&ctx, seed, sizeof(seed)); SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter)); SHA1Final(digest, &ctx); counter++; memcpy(p,digest,copylen); /* Convert to hex digits. */ for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F]; len -= copylen; p += copylen; } } else { /* If we can't read from /dev/urandom, do some reasonable effort in order to create some entropy, since this function is used to generate run_id and cluster instance IDs 如果我们不能正常读取数据,创建一些熵*/ char *x = p; unsigned int l = len; struct timeval tv; pid_t pid = getpid(); /* Use time and PID to fill the initial array. */ gettimeofday(&tv,NULL); if (l >= sizeof(tv.tv_usec)) { memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec)); l -= sizeof(tv.tv_usec); x += sizeof(tv.tv_usec); } if (l >= sizeof(tv.tv_sec)) { memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec)); l -= sizeof(tv.tv_sec); x += sizeof(tv.tv_sec); } if (l >= sizeof(pid)) { memcpy(x,&pid,sizeof(pid)); l -= sizeof(pid); x += sizeof(pid); } /* Finally xor it with rand() output, that was already seeded with time() at startup, and convert to hex digits. */ for (j = 0; j < len; j++) { p[j] ^= rand(); p[j] = charset[p[j] & 0x0F]; } } }
getabsolutepath
/* Given the filename, return the absolute path as an SDS string, or NULL * if it fails for some reason. Note that "filename" may be an absolute path * already, this will be detected and handled correctly. * * The function does not try to normalize everything, but only the obvious * case of one or more "../" appearning at the start of "filename" * relative path. */ sds getAbsolutePath(char *filename) { char cwd[1024]; sds abspath; sds relpath = sdsnew(filename); relpath = sdstrim(relpath," \r\n\t"); if (relpath[0] == '/') return relpath; /* Path is already absolute. */ /* If path is relative, join cwd and relative path. */ if (getcwd(cwd,sizeof(cwd)) == NULL) { sdsfree(relpath); return NULL; } abspath = sdsnew(cwd); if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/') abspath = sdscat(abspath,"/"); /* At this point we have the current path always ending with "/", and * the trimmed relative path. Try to normalize the obvious case of * trailing ../ elements at the start of the path. * * For every "../" we find in the filename, we remove it and also remove * the last element of the cwd, unless the current cwd is "/". */ while (sdslen(relpath) >= 3 && relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/') { sdsrange(relpath,3,-1); if (sdslen(abspath) > 1) { char *p = abspath + sdslen(abspath)-2; int trimlen = 1; while(*p != '/') { p--; trimlen++; } sdsrange(abspath,0,-(trimlen+1)); } } /* Finally glue the two parts together. */ abspath = sdscatsds(abspath,relpath); sdsfree(relpath); return abspath; } /* Return true if the specified path is just a file basename without any * relative or absolute path. This function just checks that no / or \ * character exists inside the specified path, that's enough in the * environments where Redis runs. */ int pathIsBaseName(char *path) { return strchr(path,'/') == NULL && strchr(path,'\\') == NULL; }
相关文章推荐
- 结合redis设计与实现的redis源码学习-22-集群(cluster.c)
- 结合redis设计与实现的redis源码学习-23-排序(sort.c)
- 结合redis设计与实现的redis源码学习-11-数据库(server.h/redisDb,notify.c)
- 结合redis设计与实现的redis源码学习-18-网络连接库(networking.c)
- 结合redis设计与实现的redis源码学习-1-内存分配(zmalloc)
- 结合redis设计与实现的redis源码学习-20-复制(replication.c)
- 结合redis设计与实现的redis源码学习-14-事件(ae.c/ae_epoll.c)
- 结合redis设计与实现的redis源码学习-4-dict(字典)
- 结合redis设计与实现的redis源码学习-10-hyperloglog(基数统计)
- 结合redis设计与实现的redis源码学习-11.1-命令的实现(Db.c)
- 结合redis设计与实现的redis源码学习-6-intset(整数集合)
- 结合redis设计与实现的redis源码学习-2-SDS(简单动态字符串)
- 结合redis设计与实现的redis源码学习-8.0-object(对象)
- 结合redis设计与实现的redis源码学习-17-发布与订阅(pubsub.c)
- 结合redis设计与实现的redis源码学习-8.3-t_list.c(列表键)
- 结合redis设计与实现的redis源码学习-3-链表
- 结合redis设计与实现的redis源码学习-21-哨兵(Sentinel.c)
- 结合redis设计与实现的redis源码学习-8.2-t_string(字符串键)
- 结合redis设计与实现的redis源码学习-5-skiplist(跳跃表)
- 结合redis设计与实现的redis源码学习-8.1-object.c(对象实现)