見出し画像

Unityでzstd圧縮と解凍

Unityでfacebookのzstdを利用して圧縮と解凍を行う。

CompressLevelは1(低圧縮)~22(高圧縮)を選択可能

今回はC#でzstdを使うために
https://github.com/bp74/Zstandard.Net
を利用した。

Unityで使用するためにZstandard.Netをクローンし、VisualStudioでビルド
Zstandard.Net.dll
libzstd.dll
をUnityプロジェクトのPluginフォルダにインポート

以下がUnityでzstdを用いた圧縮、解凍のサンプルコード
Spaceキーを押すとテストデータの圧縮、解凍のテストを行い、結果をDebug.Logで出力します。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using UnityEngine;
using Zstandard.Net;

public class ZstdTest : MonoBehaviour
{
    [Range(1, 22)] public int compressLevel = 11;
    public int testDataSizeKb = 10;

    private byte[] _compressed;
    private byte[] _output;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CompressTest();
        }
    }

    private void CompressTest()
    {
        var compressStartTime = DateTime.Now;

        var bytes = CreateRandomBytesWithText(testDataSizeKb * 1024);

        var log = $"Original: {ConvertUnit(bytes.Length)}\n";

        //Compress
        using (var memoryStream = new MemoryStream())
        using (var compressionStream = new ZstandardStream(memoryStream, CompressionMode.Compress))
        {
            compressionStream.CompressionLevel = compressLevel;
            compressionStream.Write(bytes, 0, bytes.Length);
            compressionStream.Close();
            _compressed = memoryStream.ToArray();

            log += $"Compressed: {ConvertUnit(bytes.Length)} -> {ConvertUnit(_compressed.Length)}\n";
            log += $"Checksum: {GetChecksum(bytes)} -> {GetChecksum(_compressed)}\n";
        }

        //Decompress
        using (var memoryStream = new MemoryStream(_compressed))
        using (var compressionStream = new ZstandardStream(memoryStream, CompressionMode.Decompress))
        using (var temp = new MemoryStream())
        {
            compressionStream.CopyTo(temp);
            _output = temp.ToArray();

            log += $"Decompressed: {ConvertUnit(_compressed.Length)} -> {ConvertUnit(_output.Length)}\n";
            log += $"Checksum: {GetChecksum(_compressed)} -> {GetChecksum(_output)}\n";
        }

        var compressEndTime = DateTime.Now;
        log += $"CompressTime: {(compressEndTime - compressStartTime).TotalMilliseconds}ms\n";

        Debug.Log(log);
    }

    private static byte[] CreateRandomBytesWithText(int size)
    {
        var bytes = new byte[size];
        var rand = new System.Random();
        const string text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        for (var i = 0; i < size; i++)
        {
            if (rand.Next(0, 100) < 50)
            {
                bytes[i] = (byte)text[rand.Next(0, text.Length)];
            }
            else
            {
                bytes[i] = (byte)rand.Next(0, 256);
            }
        }

        return bytes;
    }

    private static ushort GetChecksum(IReadOnlyList<byte> bytes)
    {
        ushort checksum = 0;
        foreach (var t in bytes)
        {
            checksum += t;
        }

        return checksum;
    }

    private static string ConvertUnit(long size)
    {
        var unit = new[] { "B", "KB", "MB", "GB", "TB" };
        var index = 0;
        var fsize = (float)size;
        while (fsize > 1024f)
        {
            fsize /= 1024f;
            index++;
        }

        return $"{fsize:F2}{unit[index]}";
    }
}

入力データにもよるが、サンプルコード内のCreateRandomBytesWithTextメソッドを使って生成したランダムデータでの圧縮時間と、圧縮サイズを計測した結果が以下。

結論CompressLevelは扱うデータによって良いレベルを選択したい。

CompressLevel = 1, 30KB
CompressLevel = 11, 30KB
CompressLevel = 22, 30KB

この記事が気に入ったらサポートをしてみませんか?