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); }
/// <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); }