Beispiel #1
0
        internal static ArraySegment <byte> Decompress(ArraySegment <byte> compressed, int headerSize, int messageSizeMax)
        {
            Debug.Assert(IsLoaded);
            int decompressedSize = InputStream.ReadInt(compressed.AsSpan(headerSize, 4));

            if (decompressedSize <= headerSize)
            {
                throw new InvalidDataException(
                          $"received compressed ice1 frame with a decompressed size of only {decompressedSize} bytes");
            }
            if (decompressedSize > messageSizeMax)
            {
                throw new InvalidDataException(
                          $"decompressed size of {decompressedSize} bytes is greater than Ice.MessageSizeMax value");
            }

            byte[] decompressed = new byte[decompressedSize];

            // Prevent GC from moving the byte array, this allow to take the object address
            // and pass it to bzip2 calls.
            var decompressedHandle = GCHandle.Alloc(decompressed, GCHandleType.Pinned);
            var compressedHandle   = GCHandle.Alloc(compressed.Array, GCHandleType.Pinned);
            var bzStream           = new BZStream(decompressedHandle.AddrOfPinnedObject() + headerSize,
                                                  (uint)(decompressedSize - headerSize));

            BzStatus rc;

            try
            {
                rc = (BzStatus)BZ2_bzDecompressInit(ref bzStream, 0, 0);
                if (rc != BzStatus.Ok)
                {
                    throw new TransportException($"bzip2 decompression failed: {rc}");
                }

                bzStream.NextIn  = compressedHandle.AddrOfPinnedObject() + compressed.Offset + headerSize + 4;
                bzStream.AvailIn = (uint)(compressed.Count - headerSize - 4);
                rc = (BzStatus)BZ2_bzDecompress(ref bzStream);
                if (rc != BzStatus.StreamEnd)
                {
                    throw new TransportException($"bzip2 decompression failed: {rc}");
                }
            }
            finally
            {
                rc = (BzStatus)BZ2_bzDecompressEnd(ref bzStream);
                Debug.Assert(rc == BzStatus.Ok);
                decompressedHandle.Free();
                compressedHandle.Free();
            }
            compressed.AsSpan(0, headerSize).CopyTo(decompressed);
            return(decompressed);
        }
Beispiel #2
0
 private static extern int BZ2_bzDecompressEnd(ref BZStream stream);
Beispiel #3
0
 private static extern int BZ2_bzDecompressInit(ref BZStream stream, int verbosity, int small);
Beispiel #4
0
 private static extern int BZ2_bzCompress(ref BZStream stream, int action);
Beispiel #5
0
 private static extern int BZ2_bzCompressInit(ref BZStream stream, int blockSize100k, int verbosity,
                                              int workFactor);
Beispiel #6
0
        internal static List <ArraySegment <byte> >?Compress(
            List <ArraySegment <byte> > data, int size, int headerSize, int compressionLevel)
        {
            Debug.Assert(IsLoaded);

            // Compress the frame body, but not the header.
            int decompressedLen = size - headerSize;

            // Compress the frame body, but not the header.
            byte[] compressed = new byte[(int)((decompressedLen * 1.01) + 600)];

            // Prevent GC from moving the byte array, this allow to take the object address and pass it to bzip2 calls.
            var compressedHandle = GCHandle.Alloc(compressed, GCHandleType.Pinned);
            var bzStream         = new BZStream(compressedHandle.AddrOfPinnedObject(), (uint)compressed.Length);

            ArraySegment <byte> headerSegment = data[0];
            BzStatus            rc;

            try
            {
                rc = (BzStatus)BZ2_bzCompressInit(ref bzStream, compressionLevel, 0, 0);
                if (rc != BzStatus.Ok)
                {
                    throw new TransportException($"bzip2 compression failed: {rc}");
                }

                // Slice the first segment to skip the header, the header is never compressed
                Debug.Assert(headerSegment.Offset == 0);
                data[0] = headerSegment.Slice(headerSize);
                rc      = BzStatus.RunOk;
                for (int i = 0; rc == BzStatus.RunOk && i < data.Count; i++)
                {
                    ArraySegment <byte> segment = data[i];
                    var segmentHandle           = GCHandle.Alloc(segment.Array, GCHandleType.Pinned);
                    bzStream.NextIn  = segmentHandle.AddrOfPinnedObject() + segment.Offset;
                    bzStream.AvailIn = (uint)segment.Count;
                    Debug.Assert(bzStream.AvailIn > 0);

                    do
                    {
                        rc = (BzStatus)BZ2_bzCompress(ref bzStream, (int)BzAction.Run);
                    }while (rc == BzStatus.RunOk && bzStream.AvailIn > 0);
                    segmentHandle.Free();
                }

                if (rc != BzStatus.RunOk)
                {
                    throw new TransportException($"bzip2 compression failed: {rc}");
                }

                do
                {
                    rc = (BzStatus)BZ2_bzCompress(ref bzStream, (int)BzAction.Finish);
                }while (rc == BzStatus.FinishOk);

                if (rc != BzStatus.StreamEnd)
                {
                    throw new TransportException($"bzip2 compression failed: {rc}");
                }

                int compressedLen = compressed.Length - (int)bzStream.AvailOut;

                // Don't bother if the compressed data is larger than the decompressed data.
                if (compressedLen >= decompressedLen)
                {
                    return(null);
                }

                // Copy the header from the decompressed stream to the compressed one, we use headerSize + 4 to ensure
                // there is room for the size of the decompressed stream in the first segment.
                ArraySegment <byte> compressedHeader = new byte[headerSize + 4];
                headerSegment.AsSpan(0, headerSize).CopyTo(compressedHeader);

                int compressedSize = compressedLen + compressedHeader.Count;
                // Write the compression status and the size of the compressed stream into the header.
                compressedHeader[9] = 2;
                OutputStream.WriteInt(compressedSize, compressedHeader.AsSpan(10, 4));

                // Write the compression status and size of the compressed stream into the header of the decompressed
                // stream -- we need this to trace requests correctly.
                headerSegment[9] = 2;
                OutputStream.WriteInt(compressedSize, headerSegment.AsSpan(10, 4));

                // Add the size of the decompressed stream before the frame body.
                OutputStream.WriteInt(size, compressedHeader.AsSpan(headerSize, 4));

                return(new List <ArraySegment <byte> >(2)
                {
                    compressedHeader,
                    new ArraySegment <byte>(compressed, 0, compressedLen)
                });
            }
            finally
            {
                // Restore the first segment that was Sliced above to skip the header
                data[0] = headerSegment;
                rc      = (BzStatus)BZ2_bzCompressEnd(ref bzStream);
                Debug.Assert(rc == BzStatus.Ok);
                compressedHandle.Free();
            }
        }