private static readonly CompressionUtils _instance = new CompressionUtils();     // only for logging

    // uncompress gzip byte array
    public static byte[] uncompress(byte[] compressedBytes)
    {
        // Semi-arbitrary choice of 16 KB for the stream window to process.  The goal here is to pick
        // a number that's large enough to reduce the number of processing iterations, but small enough
        // to avoid being a huge memory / garbage collection hit.
        const uint STREAM_WINDOW_SIZE = 16 * 1024;

        byte[] uncompressBytes = null;

        try {
            uint uncompressedSize = 0;

            // Pull the uncompressed size out of the raw GZip bytes so that we can size our output stream appropriately.
            // Without this, on larger streams the MemoryStream will get resized multiple times which thrashes the garbage collector.
            if (compressedBytes.Length > 4)
            {
                // In a gzip file, the last four bytes are the uncompressed size, assuming the
                // total uncompressed size is < 4 GB, which is a safe assumption for our data.
                // https://tools.ietf.org/html/rfc1952#page-5
                uncompressedSize =
                    (((uint)compressedBytes[compressedBytes.Length - 4]) << 0) |
                    (((uint)compressedBytes[compressedBytes.Length - 3]) << 8) |
                    (((uint)compressedBytes[compressedBytes.Length - 2]) << 16) |
                    (((uint)compressedBytes[compressedBytes.Length - 1]) << 24);
            }
            else
            {
                _instance.LogError("Compressed data too small to have length field: " + compressedBytes.Length);
            }

            uncompressBytes = new byte[uncompressedSize];

            // Uncompress the GZipped memory stream.
            using (MemoryStream stream = new MemoryStream(compressedBytes))
                using (GZipInputStream zip = new GZipInputStream(stream))
                {
                    MemoryStream outputStream = new MemoryStream(uncompressBytes);
                    StreamUtils.Copy(zip, outputStream, new byte[STREAM_WINDOW_SIZE]);
                }
        } catch (Exception e) {
            _instance.LogError("Unable to uncompress bytes: " + e.ToString());
        }

        return(uncompressBytes);
    }