/// <summary> /// Private helper used to read an int, short or byte (in reverse order) from the reader /// and return an int /// </summary> /// <param name="reader"></param> /// <param name="type"></param> /// <returns></returns> private int GetDataFromReader(BitStreamReader reader, GorillaEncodingType type) { switch (type) { case GorillaEncodingType.Int: { return((int)reader.ReadUInt32Reverse(Native.BitsPerInt)); } case GorillaEncodingType.Short: { return((int)reader.ReadUInt16Reverse(Native.BitsPerShort)); } case GorillaEncodingType.Byte: { return((int)reader.ReadByte(Native.BitsPerByte)); } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to GetDataFromReader")); } } }
/// <summary> /// Compress - compresses the byte[] being read by the BitStreamReader into compressed data /// </summary> /// <param name="bitCount">the number of bits to use for each element</param> /// <param name="reader">a reader over the byte[] to compress</param> /// <param name="encodingType">int, short or byte?</param> /// <param name="unitsToEncode">number of logical units to encoded</param> /// <param name="compressedData">output write buffer</param> internal void Compress(int bitCount, BitStreamReader reader, GorillaEncodingType encodingType, int unitsToEncode, List <byte> compressedData) { if (null == reader || null == compressedData) { throw new ArgumentNullException(StrokeCollectionSerializer.ISFDebugMessage("reader or compressedData was null in compress")); } if (bitCount < 0) { throw new ArgumentOutOfRangeException("bitCount"); } if (unitsToEncode < 0) { throw new ArgumentOutOfRangeException("unitsToEncode"); } if (bitCount == 0) { //adjust if the bitcount is 0 //(this makes bitCount 32) switch (encodingType) { case GorillaEncodingType.Int: { bitCount = Native.BitsPerInt; break; } case GorillaEncodingType.Short: { bitCount = Native.BitsPerShort; break; } case GorillaEncodingType.Byte: { bitCount = Native.BitsPerByte; break; } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to compress")); } } } //have the writer adapt to the List<byte> passed in and write to it BitStreamWriter writer = new BitStreamWriter(compressedData); while (!reader.EndOfStream && unitsToEncode > 0) { int data = GetDataFromReader(reader, encodingType); writer.Write((uint)data, bitCount); unitsToEncode--; } }
/// <summary> /// Compresses property data which is already in the form of a byte[] /// into a compressed byte[] /// </summary> /// <param name="input">byte[] data ready to be compressed</param> /// <param name="compression">the compression to use</param> /// <returns></returns> internal byte[] CompressPropertyData(byte[] input, byte compression) { List <byte> compressedData = new List <byte>(input.Length + 1); //reasonable default based on profiling. //leave room at the beginning of //compressedData for the compression header byte compressedData.Add((byte)0); if (DefaultCompression == (DefaultCompression & compression)) { compression = this.GorillaCodec.FindPropAlgoByte(input); } //validate that we never lzencode if (LempelZiv == (compression & LempelZiv)) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid compression specified or computed by FindPropAlgoByte")); } //determine what the optimal way to compress the data is. Should we treat //the byte[] as a series of Int's, Short's or Byte's? int countPerItem = 0, bitCount = 0, padCount = 0; this.GorillaCodec.GetPropertyBitCount(compression, ref countPerItem, ref bitCount, ref padCount); Debug.Assert(countPerItem == 4 || countPerItem == 2 || countPerItem == 1); GorillaEncodingType type = GorillaEncodingType.Byte; int unitCount = input.Length; if (countPerItem == 4) { type = GorillaEncodingType.Int; unitCount >>= 2; } else if (countPerItem == 2) { type = GorillaEncodingType.Short; unitCount >>= 1; } BitStreamReader reader = new BitStreamReader(input); //encode, gorilla style this.GorillaCodec.Compress(bitCount, //the max count of bits required for each int reader, //the reader, which can read int, byte, short type, //informs how the reader reads unitCount, //just how many items do we need to compress? compressedData); //a ref to the compressed data that will be written to compressedData[0] = compression; return(compressedData.ToArray()); }
/// <summary> /// Decompresses property data (from a compressed byte[] to an uncompressed byte[]) /// </summary> /// <param name="input">The byte[] to decompress</param> /// <returns></returns> internal byte[] DecompressPropertyData(byte[] input) { if (input == null) { throw new ArgumentNullException("input"); } if (input.Length < 2) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("input.Length must be at least 2")); } byte compression = input[0]; int inputIndex = 1; if (LempelZiv == (compression & LempelZiv)) { if (0 != (compression & (~LempelZiv))) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus isf, we don't decompress property data with lz")); } return(this.LZCodec.Uncompress(input, inputIndex)); } else { //gorilla //determine what the way to uncompress the data. Should we treat //the byte[] as a series of Int's, Short's or Byte's? int countPerItem = 0, bitCount = 0, padCount = 0; this.GorillaCodec.GetPropertyBitCount(compression, ref countPerItem, ref bitCount, ref padCount); Debug.Assert(countPerItem == 4 || countPerItem == 2 || countPerItem == 1); GorillaEncodingType type = GorillaEncodingType.Byte; if (countPerItem == 4) { type = GorillaEncodingType.Int; } else if (countPerItem == 2) { type = GorillaEncodingType.Short; } //determine how many units (of int, short or byte) that there are to decompress int unitsToDecode = ((input.Length - inputIndex << 3) / bitCount) - padCount; BitStreamReader reader = new BitStreamReader(input, inputIndex); return(this.GorillaCodec.Uncompress(bitCount, reader, type, unitsToDecode)); } }
/// <summary> /// Uncompress - uncompress the byte[] in the reader to a byte[] to return /// </summary> /// <param name="bitCount">number of bits each element is compressed to</param> /// <param name="reader">a reader over the compressed byte[]</param> /// <param name="encodingType">int, short or byte?</param> /// <param name="unitsToDecode">number of logical units to decode</param> /// <returns>Uncompressed byte[]</returns> internal byte[] Uncompress(int bitCount, BitStreamReader reader, GorillaEncodingType encodingType, int unitsToDecode) { if (null == reader) { throw new ArgumentNullException("reader"); } if (bitCount < 0) { throw new ArgumentOutOfRangeException("bitCount"); } if (unitsToDecode < 0) { throw new ArgumentOutOfRangeException("unitsToDecode"); } int bitsToWrite = 0; // Test whether the items are signed. For unsigned number, we don't need mask // If we are trying to compress signed long values with bit count = 5 // The mask will be 1111 1111 1111 0000. The way it is done is, if the 5th // bit is 1, the number is negative numbe, othrwise it's positive. Testing // will be non-zero, ONLY if the 5th bit is 1, in which case we OR with the mask // otherwise we leave the number as it is. uint bitMask = 0; //adjust if the bitcount is 0 //(this makes bitCount 32) switch (encodingType) { case GorillaEncodingType.Int: { if (bitCount == 0) { bitCount = Native.BitsPerInt; } bitsToWrite = Native.BitsPerInt; //we decode int's as unsigned, so we need to create a mask bitMask = (unchecked((uint)~0) << (bitCount - 1)); break; } case GorillaEncodingType.Short: { if (bitCount == 0) { bitCount = Native.BitsPerShort; } bitsToWrite = Native.BitsPerShort; //shorts are decoded as unsigned values, no mask required bitMask = 0; break; } case GorillaEncodingType.Byte: { if (bitCount == 0) { bitCount = Native.BitsPerByte; } bitsToWrite = Native.BitsPerByte; //bytes are decoded as unsigned values, no mask required bitMask = 0; break; } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to Uncompress")); } } List<byte> output = new List<byte>((bitsToWrite / 8) * unitsToDecode); BitStreamWriter writer = new BitStreamWriter(output); uint bitData = 0; while (!reader.EndOfStream && unitsToDecode > 0) { //we're going to cast to an uint anyway, just read as one bitData = reader.ReadUInt32(bitCount); // Construct the item bitData = ((bitData & bitMask) != 0) ? bitMask | bitData : bitData; writer.WriteReverse(bitData, bitsToWrite); unitsToDecode--; } return output.ToArray(); }
/// <summary> /// Private helper used to read an int, short or byte (in reverse order) from the reader /// and return an int /// </summary> /// <param name="reader"></param> /// <param name="type"></param> /// <returns></returns> private int GetDataFromReader(BitStreamReader reader, GorillaEncodingType type) { switch (type) { case GorillaEncodingType.Int: { return (int)reader.ReadUInt32Reverse(Native.BitsPerInt); } case GorillaEncodingType.Short: { return (int)reader.ReadUInt16Reverse(Native.BitsPerShort); } case GorillaEncodingType.Byte: { return (int)reader.ReadByte(Native.BitsPerByte); } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to GetDataFromReader")); } } }
/// <summary> /// Compress - compresses the byte[] being read by the BitStreamReader into compressed data /// </summary> /// <param name="bitCount">the number of bits to use for each element</param> /// <param name="reader">a reader over the byte[] to compress</param> /// <param name="encodingType">int, short or byte?</param> /// <param name="unitsToEncode">number of logical units to encoded</param> /// <param name="compressedData">output write buffer</param> internal void Compress(int bitCount, BitStreamReader reader, GorillaEncodingType encodingType, int unitsToEncode, List<byte> compressedData) { if (null == reader || null == compressedData) { throw new ArgumentNullException(StrokeCollectionSerializer.ISFDebugMessage("reader or compressedData was null in compress")); } if (bitCount < 0) { throw new ArgumentOutOfRangeException("bitCount"); } if (unitsToEncode < 0) { throw new ArgumentOutOfRangeException("unitsToEncode"); } if (bitCount == 0) { //adjust if the bitcount is 0 //(this makes bitCount 32) switch (encodingType) { case GorillaEncodingType.Int: { bitCount = Native.BitsPerInt; break; } case GorillaEncodingType.Short: { bitCount = Native.BitsPerShort; break; } case GorillaEncodingType.Byte: { bitCount = Native.BitsPerByte; break; } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to compress")); } } } //have the writer adapt to the List<byte> passed in and write to it BitStreamWriter writer = new BitStreamWriter(compressedData); while (!reader.EndOfStream && unitsToEncode > 0) { int data = GetDataFromReader(reader, encodingType); writer.Write((uint)data, bitCount); unitsToEncode--; } }
/// <summary> /// Uncompress - uncompress the byte[] in the reader to a byte[] to return /// </summary> /// <param name="bitCount">number of bits each element is compressed to</param> /// <param name="reader">a reader over the compressed byte[]</param> /// <param name="encodingType">int, short or byte?</param> /// <param name="unitsToDecode">number of logical units to decode</param> /// <returns>Uncompressed byte[]</returns> internal byte[] Uncompress(int bitCount, BitStreamReader reader, GorillaEncodingType encodingType, int unitsToDecode) { if (null == reader) { throw new ArgumentNullException("reader"); } if (bitCount < 0) { throw new ArgumentOutOfRangeException("bitCount"); } if (unitsToDecode < 0) { throw new ArgumentOutOfRangeException("unitsToDecode"); } int bitsToWrite = 0; // Test whether the items are signed. For unsigned number, we don't need mask // If we are trying to compress signed long values with bit count = 5 // The mask will be 1111 1111 1111 0000. The way it is done is, if the 5th // bit is 1, the number is negative numbe, othrwise it's positive. Testing // will be non-zero, ONLY if the 5th bit is 1, in which case we OR with the mask // otherwise we leave the number as it is. uint bitMask = 0; //adjust if the bitcount is 0 //(this makes bitCount 32) switch (encodingType) { case GorillaEncodingType.Int: { if (bitCount == 0) { bitCount = Native.BitsPerInt; } bitsToWrite = Native.BitsPerInt; //we decode int's as unsigned, so we need to create a mask bitMask = (unchecked ((uint)~0) << (bitCount - 1)); break; } case GorillaEncodingType.Short: { if (bitCount == 0) { bitCount = Native.BitsPerShort; } bitsToWrite = Native.BitsPerShort; //shorts are decoded as unsigned values, no mask required bitMask = 0; break; } case GorillaEncodingType.Byte: { if (bitCount == 0) { bitCount = Native.BitsPerByte; } bitsToWrite = Native.BitsPerByte; //bytes are decoded as unsigned values, no mask required bitMask = 0; break; } default: { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("bogus GorillaEncodingType passed to Uncompress")); } } List <byte> output = new List <byte>((bitsToWrite / 8) * unitsToDecode); BitStreamWriter writer = new BitStreamWriter(output); uint bitData = 0; while (!reader.EndOfStream && unitsToDecode > 0) { //we're going to cast to an uint anyway, just read as one bitData = reader.ReadUInt32(bitCount); // Construct the item bitData = ((bitData & bitMask) != 0) ? bitMask | bitData : bitData; writer.WriteReverse(bitData, bitsToWrite); unitsToDecode--; } return(output.ToArray()); }