Exemplo n.º 1
0
        public static int Decode(byte[] source, int sourceOffset, int sourceLength, byte[] destination, int destinationOffset, int destinationLength)
        {
            // Get the uncompressed size
            var len = VarInt.UvarInt(source, sourceOffset);

            // Destination array doesn't have enough room for the decoded data.
            if (destinationLength < (int)len.Value)
            {
                throw new IndexOutOfRangeException("snappy: destination array too short");
            }

            // Adjust the lengths
            sourceLength      += sourceOffset;
            destinationLength += destinationOffset;

            // Current offsets
            int s      = sourceOffset + len.VarIntLength;
            int d      = destinationOffset;
            int offset = 0;
            int length = 0;

            while (s < sourceLength)
            {
                byte tag = (byte)(source[s] & 0x03);
                if (tag == SnappyTag.Literal)
                {
                    uint x = (uint)(source[s] >> 2);
                    if (x < 60)
                    {
                        s += 1;
                    }
                    else if (x == 60)
                    {
                        s += 2;
                        if (s > sourceLength)
                        {
                            throw new IndexOutOfRangeException("snappy: corrupt input");
                        }
                        x = (uint)source[s - 1];
                    }
                    else if (x == 61)
                    {
                        s += 3;
                        if (s > sourceLength)
                        {
                            throw new IndexOutOfRangeException("snappy: corrupt input");
                        }
                        x = (uint)source[s - 2] | (uint)source[s - 1] << 8;
                    }
                    else if (x == 62)
                    {
                        s += 4;
                        if (s > sourceLength)
                        {
                            throw new IndexOutOfRangeException("snappy: corrupt input");
                        }
                        x = (uint)source[s - 3] | (uint)source[s - 2] << 8 | (uint)source[s - 1] << 16;
                    }
                    else if (x == 63)
                    {
                        s += 5;
                        if (s > sourceLength)
                        {
                            throw new IndexOutOfRangeException("snappy: corrupt input");
                        }
                        x = (uint)source[s - 4] | (uint)source[s - 3] << 8 | (uint)source[s - 2] << 16 | (uint)source[s - 1] << 24;
                    }
                    length = (int)(x + 1);
                    if (length <= 0)
                    {
                        throw new IndexOutOfRangeException("snappy: unsupported literal length");
                    }
                    if (length > destinationLength - d || length > sourceOffset + sourceLength - s)
                    {
                        throw new IndexOutOfRangeException("snappy: corrupt input");
                    }
                    Array.Copy(source, s, destination, d, length);
                    d += length;
                    s += length;
                    continue;
                }
                else if (tag == SnappyTag.Copy1)
                {
                    s += 2;
                    if (s > sourceOffset + sourceLength)
                    {
                        throw new IndexOutOfRangeException("snappy: corrupt input");
                    }
                    length = 4 + (((int)source[s - 2] >> 2) & 0x7);
                    offset = ((int)source[s - 2] & 0xe0) << 3 | (int)source[s - 1];
                }
                else if (tag == SnappyTag.Copy2)
                {
                    s += 3;
                    if (s > sourceOffset + sourceLength)
                    {
                        throw new IndexOutOfRangeException("snappy: corrupt input");
                    }
                    length = 1 + ((int)source[s - 3] >> 2);
                    offset = (int)source[s - 2] | (int)source[s - 1] << 8;
                }
                else if (tag == SnappyTag.Copy4)
                {
                    throw new NotSupportedException("snappy: unsupported COPY_4 tag");
                }

                int end = d + length;
                if (offset > d || end > destinationLength)
                {
                    throw new IndexOutOfRangeException("snappy: corrupt input");
                }

                for (; d < end; d++)
                {
                    destination[d] = destination[d - offset];
                }
            }

            return(d);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Encodes the buffer into the stream provided.
        /// </summary>
        /// <param name="source">The source buffer.</param>
        /// <param name="sourceOffset">The offset to start encoding.</param>
        /// <param name="sourceLength">The number of bytes to encode.</param>
        /// <param name="destination">The destination to write into.</param>
        /// <param name="destinationOffset">The offset to start writing into.</param>
        /// <returns></returns>
        public int Encode(byte[] source, int sourceOffset, int sourceLength, BufferSegment destination, int destinationOffset)
        {
            if (destination.Size - destinationOffset < MaxEncodedLen(sourceLength))
            {
                throw new IndexOutOfRangeException("Snappy destination array too short.");
            }

            // The block starts with the varint-encoded length of the decompressed bytes.
            var start = this.Index = destination.Offset + destinationOffset;

            this.Index += VarInt.WriteTo(destination, this.Index, (ulong)sourceLength);

            // Return early if src is short.
            if (sourceLength <= 4 && sourceLength != 0)
            {
                EmitLiteral(destination, source, sourceOffset, sourceLength);
                return(this.Index - start);
            }

            // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
            int  shift     = 32 - 8;
            uint tableSize = 1 << 8;

            while (tableSize < MaxTableSize && tableSize < sourceLength)
            {
                shift--;
                tableSize *= 2;
            }

            // Clear the table up until we're going to use it, to save time
            Array.Clear(Table, 0, (int)tableSize);

            // Iterate over the source bytes.
            int s   = sourceOffset;
            int t   = 0;
            int lit = sourceOffset;

            while (s + 3 < sourceLength)
            {
                // Update the hash table.
                byte b0 = source[s];
                byte b1 = source[s + 1];
                byte b2 = source[s + 2];
                byte b3 = source[s + 3];
                uint h  = (uint)b0 | ((uint)b1) << 8 | ((uint)b2) << 16 | ((uint)b3) << 24;
                var  p  = (h * 0x1e35a7bd) >> shift;

                // We need to to store values in [-1, inf) in table. To save
                // some initialization time, (re)use the table's zero value
                // and shift the values against this zero: add 1 on writes,
                // subtract 1 on reads.
                t             = this.Table[p] - 1;
                this.Table[p] = s + 1;

                // If t is invalid or src[s:s+4] differs from src[t:t+4],
                // accumulate a literal byte.
                if (t < 0 || s - t >= MaxOffset || b0 != source[t] || b1 != source[t + 1] || b2 != source[t + 2] || b3 != source[t + 3])
                {
                    s++;
                    continue;
                }

                // Otherwise, we have a match. First, emit any pending literal bytes.
                if (lit != s)
                {
                    EmitLiteral(destination, source, lit, s - lit);
                }

                // Extend the match to be as long as possible.
                int s0 = s;
                s = s + 4;
                t = t + 4;
                while (s < sourceLength && source[s] == source[t])
                {
                    s++;
                    t++;
                }

                // Emit the copied bytes.
                EmitCopy(destination, s - t, s - s0 + sourceOffset);
                lit = s;
            }

            // Emit any final pending literal bytes and return.
            if (lit != sourceLength)
            {
                EmitLiteral(destination, source, lit, sourceLength - lit);
            }

            return(this.Index - start);
        }