redis6.0.5之lzf阅读笔记3--解压缩
********************lzf.h************************************************************************************************* #include "lzfP.h" #if AVOID_ERRNO # define SET_ERRNO(n) #else # include <errno.h> # define SET_ERRNO(n) errno = (n) #endif #if USE_REP_MOVSB /* small win on amd, big loss on intel */ #if (__i386 || __amd64) && __GNUC__ >= 3 # define lzf_movsb(dst, src, len) \ asm ("rep movsb" \ : "=D" (dst), "=S" (src), "=c" (len) \ : "0" (dst), "1" (src), "2" (len)); #endif #endif #if defined(__GNUC__) && __GNUC__ >= 5 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif unsigned int lzf_decompress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len) { u8 const *ip = (const u8 *)in_data; 输入数据开始位置 u8 *op = (u8 *)out_data; 输出数据开始位置 u8 const *const in_end = ip + in_len; 输入数据结束位置 u8 *const out_end = op + out_len; 输出缓冲区结束位置 do { unsigned int ctrl = *ip++; 获取标志位 if (ctrl < (1 << 5)) /* literal run */ 原文表示,因为值小于2^5,所以是原文表示 { ctrl++; 长度加1 if (op + ctrl > out_end) 是否超过缓冲区 { SET_ERRNO (E2BIG); 提示错误 return 0; } #if CHECK_INPUT if (ip + ctrl > in_end) 如果当前位置加上将要解压的长度超过 输入的缓冲区,说明出错 { SET_ERRNO (EINVAL); return 0; } #endif #ifdef lzf_movsb 如果定义了更快的拷贝方法,就使用更快的方法 lzf_movsb (op, ip, ctrl); #else switch (ctrl) 挨个拷贝原文字符串 { case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++; case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++; case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++; case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++; case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++; case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case 9: *op++ = *ip++; case 8: *op++ = *ip++; case 7: *op++ = *ip++; case 6: *op++ = *ip++; case 5: *op++ = *ip++; case 4: *op++ = *ip++; case 3: *op++ = *ip++; case 2: *op++ = *ip++; case 1: *op++ = *ip++; } #endif } else /* back reference */ 非原文字符,需要根据偏移量和长度来拷贝 { unsigned int len = ctrl >> 5; 首先获取长度或者控制字符 u8 *ref = op - ((ctrl & 0x1f) << 8) - 1; 偏移量的高5位 再减去1,进行回退(真正的偏移量还差低字节) #if CHECK_INPUT if (ip >= in_end) 如果已经到或者超过结束了,那么解压出错 { SET_ERRNO (EINVAL); return 0; } #endif if (len == 7) 如果是长序列压缩 7就是111就是长序列 { len += *ip++; 下一个字节就是长度 #if CHECK_INPUT if (ip >= in_end) { SET_ERRNO (EINVAL); return 0; } #endif } ref -= *ip++; 低字节的偏移量也要回退 if (op + len + 2 > out_end) 当前位置加解压长度加2超过 输出缓冲区结尾,解压出错 { SET_ERRNO (E2BIG); return 0; } if (ref < (u8 *)out_data) 如果引用值小于原字符序列开始值,解压也出错 { SET_ERRNO (EINVAL); return 0; } #ifdef lzf_movsb len += 2; 长度值需要加2 lzf_movsb (op, ref, len); #else switch (len) { default: len += 2; if (op >= ref + len) 如果当前操作符所在位置大于 引用值 + 当前解压长度 所在位置, 那么表示前面重复的数据已经全部解压出来,否则就有重叠的数据还没有解压出来,需要挨个解压 { /* disjunct areas */ 分开区域 即 不重合区域 memcpy (op, ref, len); 直接拷贝即可 op += len; } else { 重合区域,一个字节一个字节的拷贝,就是压缩的时候引用了自身,例如 a123123123123abc,这里123123123就会被自身引用,导致数据 /* overlapping, use octte by octte copying */ do *op++ = *ref++; while (--len); } break; case 9: *op++ = *ref++; /* fall-thru */ 依次 下降拷贝 case 8: *op++ = *ref++; /* fall-thru */ case 7: *op++ = *ref++; /* fall-thru */ case 6: *op++ = *ref++; /* fall-thru */ case 5: *op++ = *ref++; /* fall-thru */ case 4: *op++ = *ref++; /* fall-thru */ case 3: *op++ = *ref++; /* fall-thru */ case 2: *op++ = *ref++; /* fall-thru */ case 1: *op++ = *ref++; /* fall-thru */ case 0: *op++ = *ref++; /* two octets more */ 这里的两个字节就是加2的效果 *op++ = *ref++; /* fall-thru */ } #endif } } while (ip < in_end); return op - (u8 *)out_data; } #if defined(__GNUC__) && __GNUC__ >= 5 #pragma GCC diagnostic pop #endif *********************************************************************************************************************