【问题标题】:Decompress data in Span<byte> with Deflate algorithm使用 Deflate 算法解压 Span<byte> 中的数据
【发布时间】:2018-06-16 14:51:46
【问题描述】:

我有一个解析二进制文件的代码。它使用Span&lt;byte&gt; 在函数之间传递数据块以避免不必要的内存分配。

简化示例:

class Reader {
    bool IsCompressed { get; set; }
    public byte[] Data { get; set; }

    function ReadOnlySpan<byte> ReadBlock() {
        ...
    }

    public function Read() {
        if (IsCompressed) {
            Data = ???
        } else {
             Data = ReadBlock.ToArray();
        }
}

我可以用Syste.IO.Compression.DeflateStream解压数据,但是这个类只接受Stream作为输入,所以我必须将ReadOnlySpan&lt;byte&gt;转换为流,从而为byte[]分配内存。

using(var stream = new MemoryStream(buffer.ToArray())) {
    using(var deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) {
        Data = ... read from deflateStream
    }
}

有没有办法将ReadOnlySpan&lt;byte&gt; 传递给DeflateStream 而不会分配不必要的内存?或者有没有其他库可以直接从ReadOnlySpan&lt;byte&gt;解压数据?

我正在使用 .NET Core 2.1.0-preview1-26103-03,但如果需要,我可以使用任何其他版本。

【问题讨论】:

    标签: c# .net-core c#-7.2


    【解决方案1】:

    截至 2018 年 1 月,没有对 ReadOnlySpan&lt;byte&gt; 的任何内置支持,但正如 Stephen Toub 在 GitHub 上指出的那样,它可以通过 UnmanagedMemoryStream 解决

    private unsafe byte[] Decompress(ReadOnlySpan<byte> buffer, int decompressedSize) {
        fixed (byte* pBuffer = &buffer[0]) {
            using (var stream = new UnmanagedMemoryStream(pBuffer, buffer.Length)) {
                using (var deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) {
                    var data = ArrayPool<byte>.Shared.Rent(decompressedSize);
                    deflateStream.Read(data, 0, decompressedSize);
    
                    return data;
                }
            }
        }
    }
    

    【讨论】:

    • 如何确定decompressedSize
    • 就我而言,我知道解压缩后的大小,因为压缩的数据块是一种更复杂的文件格式,它与压缩数据一起存储解压缩后的大小。这可能会对您有所帮助:stackoverflow.com/a/33444863/247656
    • 据我了解,您返回的是租用的数组,调用者没有返回数组怎么办?
    • @shtse8 在一般情况下,这可能是个问题,但是您可以看到我的代码 sn-p 包含私有函数,因此我可以保证将数组返回到池中。
    • @LukasKabrt 我建议你应该在这里打个线来提醒想要接受这个答案的人,否则可能会导致内存泄漏,因为租用的数组没有返回潜力。
    【解决方案2】:

    我不确定是否需要非托管代码,或许可以考虑这样:

    using var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApp.Resources.Data.zip");
    if (resource is null)
        throw new ResourceNotFoundException("Data.Zip", Assembly.GetExecutingAssembly());
    
    using (ZipArchive archive = new System.IO.Compression.ZipArchive(stream: resource))
    {
        foreach (var entry in archive.Entries)
        {
            if (string.Equals(entry.Name, "My.json", StringComparison.InvariantCultureIgnoreCase))
            {
                using var unZip = entry.Open();
                var bytes=new byte[(int)entry.CompressedLength].AsSpan();
                unZip.Read(bytes);                               
                var data= Newtonsoft.Json.JsonConvert.DeserializeObject<MyObject>(System.Text.Encoding.UTF8.GetString(bytes));
            }
        }
    }
    

    性能不错,大大降低了嵌入式资源的大小

    【讨论】:

      猜你喜欢
      • 2020-06-16
      • 2017-11-08
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多