C#重写Lz4 JavaScript文本压缩算法(一)

758 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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);
        }
    }
}

lz4.js -> Lz4.cs(核心逻辑)

util.js -> Util.cs(工具类及测试)