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
*********************************************************************************************************************

 

posted on 2021-07-12 17:27  子虚乌有  阅读(115)  评论(0)    收藏  举报