lz4算法实现

概述

lz4算法是lz77算法的一种实现,就是查找重复的字符串,重复的字符串使用(距离,长度)来表示。
比如abcdefgabcdefg,被压缩后就表示成了:
abcdefg,(1,7)
距离有多重表示方法:

  1. 重复的字符串尾部距离当前正在处理的字符的距离
  2. 重复的字符串头部距离当前正在处理的字符的距离
  3. 重复的字符串在输入字符串的位置
  4. 重复字符串头部位置的内存地址
    lz77更详细的资料,可以查看其它博文,本文主要讲lz4中的实现。

lz4的实现

数据结构(hash)

LZ4最小匹配长度设的是4个字节,只有重复的字符串长度大于等于4,才认为是找到了重复字符串。
LZ4使用了hashtable,来存储之前出现过的字符串(4个字节)。hashtable实际上就是java或c++中的Map。
在这里插入图片描述

key值的计算

LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
{
    U32 const h = LZ4_hashPosition(p, tableType);
    LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
}

LZ4_hashPosition函数就是计算key值,是使用p指针所指向的4个字节的值来计算的,代表4个byte的值。
LZ4_putPositionOnHash函数就是向hashtable中存入value,value是代表的是字节的位置。

value的存储

hashtable中的value代表的是字节的位置,字节的位置有两种表示方式:
1.字节的绝对地址(指针)
2.字节在数据中的索引。
LZ4按最小表示长度来选择的,这样hashtable就可以存储更多的值,降低哈希碰撞

if (maxOutputSize >= LZ4_compressBound(inputSize)) {
    if (inputSize < LZ4_64Klimit) {
        return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
    } else {
        const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
        return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
    }
} else {
    if (inputSize < LZ4_64Klimit) {
        return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
    } else {
        const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
        return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
    }
}
  1. src的长度小于64K,这样可以用16位表示,因此使用byU16策略,就是使用16位(short)存储数组索引
  2. 如果src的长度大于64K且指针类型的长度是4个字节(byPtr),则直接存储字节的地址
    备注:因为存在hash碰撞,所以通过key值获取到字节后,还要进行值的对比。这里如果字节存储指针,到时候进行值的对比的时候,会少一个计算。
  3. 如果src的长度大约64K,且指针类型的长度不为4个字节(不为4个字节,就肯定大约4个字节了,不然src的长度,不可能大于64K),这直接存储32位数组索引(byU32)。
LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,
                                  void* tableBase, tableType_t const tableType,
                            const BYTE* srcBase)
{
    switch (tableType)
    {
    case clearedTable: { /* illegal! */ assert(0); return; }
    # 直接存储指针
    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
    # 存储在数组中的索引
    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
    }
}

算法流程

  1. 计算当前4个字节的hash值,然后查询hashtable中的value
  2. 根据value得到字节的位置
  3. 对比当前4个字节和通过value转换得到的4个字节的值(因为有hash碰撞,所以要校验一下值)
  4. 如果两者相等,则继续对比后面的字节,找出最长的匹配。然后根据LZ4的格式,写入三元组(未匹配的字符串,匹配字符串的长度,匹配的字符串的位置)
  5. 如果两者不相等,这向后移动一个字节,继续进行匹配。当前的字节归为未匹配的字符,并将当前的4个字节存储到hashtable中。
    在这里插入图片描述

算法档位

LZ4档位决定了上面截图中的step。档位越高,step越小(大家对着代码证明一下)。
当前位置未找到重复字符串时,会向后移动step个字节,再次进行匹配。
如果step越大,跳过的字节数越多,进行查找的次数就越少,速度越快。
压缩率会下降,因为被跳过的字节被归为未匹配的字节,但实际上它是有可能找到匹配的。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值