本文已参与「新人创作礼」活动,一起开启掘金创作之路。
C#重写Lz4 JavaScript文本压缩算法(一) - 掘金 (juejin.cn)
C#重写Lz4 JavaScript文本压缩算法(二) - 掘金 (juejin.cn)
C#重写Lz4 JavaScript文本压缩算法(三) - 掘金 (juejin.cn)
前言
工作需要,由于复杂度提升,公司算法端生成的结果会有几十甚至上百M,直接将结果发送到网页端,太占用网络资源。因此考虑将C#端的结果文本进行压缩,发送给前端。通过比较压缩/解压速度,最后选取了Lz4压缩算法。
网上找了一些资料,C#端用lz4net这个库,js端用这个库lz4js。
近一步测试,发现这两个库只能压缩/解压自己的内容,并不能相互压缩/解压。通过对C#端lz4net库的dll文件的反编译,发现其压缩算法和js端稍有差异,导致无法相互解析。
综合以上,只要保证C#和JS两边算法一致即可相互解析。由于C#端算法阅读性差,最终决定将js的lz4压缩算法用C#重写一遍。为方便理解,特意留了原版注释。
xxh32.js -> Xxh32.cs
using System;
namespace Lz4CSharp
{
class Xxh32
{
// xxhash32 primes
static UInt32 prime1 = 0x9e3779b1;
static UInt32 prime2 = 0x85ebca77;
static UInt32 prime3 = 0xc2b2ae3d;
static UInt32 prime4 = 0x27d4eb2f;
static UInt32 prime5 = 0x165667b1;
// Utility functions/primitives
// --
public static UInt32 rotl32(UInt32 x, int r)
{
x |= 0;
r |= 0;
return Util.UInt32MoveRight(x, 32 - r | 0) | x << r | 0;
}
public static UInt32 rotmul32(UInt32 h, int r, UInt32 m)
{
h |= 0;
r |= 0;
m |= 0;
return Util.imul(Util.UInt32MoveRight(h, 32 - r | 0) | h << r, m) | 0;
}
public static UInt32 shiftxor32(UInt32 h, int s)
{
h |= 0;
s |= 0;
return Util.UInt32MoveRight(h, s) ^ h | 0;
}
// Implementation
// --
public static UInt32 xxhapply(UInt32 h, UInt32 src, UInt32 m0, int s, UInt32 m1)
{
return rotmul32(Util.imul(src, m0) + h, s, m1);
}
public static UInt32 xxh1(UInt32 h, UInt32[] src, int index)
{
return rotmul32((h + Util.imul(src[index], prime5)), 11, prime1);
}
public static UInt32 xxh4(UInt32 h, UInt32[] src, int index)
{
return xxhapply(h, Util.readU32(src, index), prime3, 17, prime4);
}
public static UInt32[] xxh16(UInt32[] h, UInt32[] src, int index)
{
return new UInt32[] {
xxhapply(h[0], Util.readU32(src, index + 0), prime2, 13, prime1),
xxhapply(h[1], Util.readU32(src, index + 4), prime2, 13, prime1),
xxhapply(h[2], Util.readU32(src, index + 8), prime2, 13, prime1),
xxhapply(h[3], Util.readU32(src, index + 12), prime2, 13, prime1)
};
}
public static UInt32 xxh32(UInt32 seed, UInt32[] src, int index, int len)
{
UInt32 h;
UInt32 l = (UInt32)len;
if (len >= 16)
{
UInt32[] tmpH = new UInt32[] {
seed + prime1 + prime2,
seed + prime2,
seed,
seed - prime1
};
while (len >= 16)
{
tmpH = xxh16(tmpH, src, index);
index += 16;
len -= 16;
}
h = rotl32(tmpH[0], 1) + rotl32(tmpH[1], 7) + rotl32(tmpH[2], 12) + rotl32(tmpH[3], 18) + l;
}
else
{
h = Util.UInt32MoveRight(seed + prime5 + (UInt32)len, 0);
}
while (len >= 4)
{
h = xxh4(h, src, index);
index += 4;
len -= 4;
}
while (len > 0)
{
h = xxh1(h, src, index);
index++;
len--;
}
h = shiftxor32(Util.imul(shiftxor32(Util.imul(shiftxor32(h, 15), prime2), 13), prime3), 16);
return Util.UInt32MoveRight(h , 0);
}
}
}