27 using SiliconStudio.Core.IO;
29 namespace SiliconStudio.Core.LZ4
50 HighCompression = 0x02,
54 Passes = 0x04 | 0x08 | 0x10,
62 private readonly
Stream innerStream;
65 private readonly CompressionMode compressionMode;
68 private readonly
bool highCompression;
73 private readonly
bool disposeInnerStream;
76 private readonly
int blockSize;
79 private byte[] dataBuffer;
82 private byte[] compressedDataBuffer;
85 private int bufferLength;
88 private int bufferOffset;
93 private long position;
98 private long innerStreamPosition;
103 private readonly
long length;
108 private readonly
long compressedSize;
123 CompressionMode compressionMode,
124 bool highCompression =
false,
125 long uncompressedSize = -1,
126 long compressedSize = -1,
127 bool disposeInnerStream =
false,
128 int blockSize = 1024*1024)
130 this.innerStream = innerStream;
131 this.compressionMode = compressionMode;
132 this.highCompression = highCompression;
133 this.blockSize = Math.Max(16, blockSize);
134 length = uncompressedSize;
135 this.compressedSize = compressedSize;
136 this.disposeInnerStream = disposeInnerStream;
146 private static NotSupportedException NotSupported(
string operationName)
148 return new NotSupportedException(
150 "Operation '{0}' is not supported", operationName));
155 private static EndOfStreamException EndOfStream()
157 return new EndOfStreamException(
"Unexpected end of stream");
165 private bool TryReadVarInt(out ulong result)
167 var buffer =
new byte[1];
173 if ((compressedSize != -1 && innerStreamPosition >= compressedSize) || innerStream.Read(buffer, 0, 1) == 0)
175 if (count == 0)
return false;
178 innerStreamPosition += 1;
180 result = result + ((ulong)(b & 0x7F) <<
count);
182 if ((b & 0x80) == 0 || count >= 64)
break;
192 private ulong ReadVarInt()
195 if (!TryReadVarInt(out result))
throw EndOfStream();
206 private int ReadBlock(byte[] buffer,
int offset,
int count)
212 var read = innerStream.Read(buffer, offset,
count);
213 if (read == 0)
break;
214 innerStreamPosition += read;
225 private void WriteVarInt(ulong value)
227 var buffer =
new byte[1];
230 var b = (byte)(value & 0x7F);
232 buffer[0] = (byte)(b | (value == 0 ? 0 : 0x80));
233 innerStream.Write(buffer, 0, 1);
234 innerStreamPosition += 1;
235 if (value == 0)
break;
240 private void FlushCurrentChunk()
242 if (bufferOffset <= 0)
return;
244 var compressed =
new byte[bufferOffset];
245 var compressedLength = highCompression
246 ? LZ4Codec.EncodeHC(dataBuffer, 0, bufferOffset, compressed, 0, bufferOffset)
247 : LZ4Codec.Encode(dataBuffer, 0, bufferOffset, compressed, 0, bufferOffset);
249 if (compressedLength <= 0 || compressedLength >= bufferOffset)
252 compressed = dataBuffer;
253 compressedLength = bufferOffset;
256 var isCompressed = compressedLength < bufferOffset;
258 var
flags = ChunkFlags.None;
260 if (isCompressed) flags |= ChunkFlags.Compressed;
261 if (highCompression) flags |= ChunkFlags.HighCompression;
263 WriteVarInt((ulong)flags);
264 WriteVarInt((ulong)bufferOffset);
265 if (isCompressed) WriteVarInt((ulong)compressedLength);
267 innerStream.Write(compressed, 0, compressedLength);
268 innerStreamPosition += compressedLength;
276 private bool AcquireNextChunk()
281 if (!TryReadVarInt(out varint))
return false;
282 var flags = (ChunkFlags)varint;
283 var isCompressed = (flags & ChunkFlags.Compressed) != 0;
285 var originalLength = (int)ReadVarInt();
286 var compressedLength = isCompressed ? (int)ReadVarInt() : originalLength;
287 if (compressedLength > originalLength)
throw EndOfStream();
289 if (compressedDataBuffer == null || compressedDataBuffer.Length < compressedLength)
290 compressedDataBuffer =
new byte[compressedLength];
291 var chunk = ReadBlock(compressedDataBuffer, 0, compressedLength);
293 if (chunk != compressedLength)
throw EndOfStream();
298 var oldDataBuffer = dataBuffer;
299 dataBuffer = compressedDataBuffer;
300 compressedDataBuffer = oldDataBuffer;
302 bufferLength = compressedLength;
306 if (dataBuffer == null || dataBuffer.Length < originalLength)
307 dataBuffer =
new byte[originalLength];
308 var passes = (int)flags >> 2;
310 throw new NotSupportedException(
"Chunks with multiple passes are not supported.");
311 LZ4Codec.Decode(compressedDataBuffer, 0, compressedLength, dataBuffer, 0, originalLength,
true);
312 bufferLength = originalLength;
316 }
while (bufferLength == 0);
327 public override bool CanRead
334 public override bool CanSeek
336 get {
return false; }
341 public override bool CanWrite
349 if (bufferOffset > 0 && CanWrite) FlushCurrentChunk();
354 public override long Length
356 get {
return length; }
361 public override long Position
363 get {
return position; }
364 set {
throw NotSupported(
"SetPosition"); }
371 if (!CanRead)
throw NotSupported(
"Read");
373 if (bufferOffset >= bufferLength && !AcquireNextChunk())
378 return dataBuffer[bufferOffset++];
386 public override unsafe
int Read(byte[] buffer,
int offset,
int count)
388 if (!CanRead)
throw NotSupported(
"Read");
394 var chunk = Math.Min(
count, bufferLength - bufferOffset);
397 fixed (byte* pSrc = dataBuffer)
398 fixed (byte* pDst = buffer)
400 Utilities.CopyMemory((IntPtr)pDst + offset, (IntPtr)pSrc + bufferOffset, chunk);
402 bufferOffset += chunk;
409 if (!AcquireNextChunk())
break;
418 public unsafe
override int Read(IntPtr buffer,
int count)
420 if (!CanRead)
throw NotSupported(
"Read");
426 var chunk = Math.Min(
count, bufferLength - bufferOffset);
429 fixed (byte* pSrc = dataBuffer)
431 Utilities.CopyMemory((IntPtr)buffer + total, (IntPtr)pSrc + bufferOffset, chunk);
433 bufferOffset += chunk;
439 if (!AcquireNextChunk())
break;
451 public override long Seek(
long offset, SeekOrigin origin)
456 case SeekOrigin.Begin:
457 newPosition = offset;
459 case SeekOrigin.Current:
460 newPosition = Position + offset;
463 throw NotSupported(
"Seek");
465 throw new ArgumentOutOfRangeException(
"origin");
468 if (newPosition == 0)
470 innerStream.Seek(-innerStreamPosition, SeekOrigin.Current);
473 else if (newPosition == Position)
479 throw NotSupported(
"Seek");
489 throw NotSupported(
"SetLength");
496 if (!CanWrite)
throw NotSupported(
"Write");
500 if (dataBuffer == null)
502 dataBuffer =
new byte[blockSize];
503 bufferLength = blockSize;
507 if (bufferOffset >= bufferLength)
512 dataBuffer[bufferOffset++] = value;
519 public override unsafe
void Write(byte[] buffer,
int offset,
int count)
521 if (!CanWrite)
throw NotSupported(
"Write");
525 if (dataBuffer == null)
527 dataBuffer =
new byte[blockSize];
528 bufferLength = blockSize;
534 var chunk = Math.Min(
count, bufferLength - bufferOffset);
537 fixed (byte* pSrc = buffer)
538 fixed (byte* pDst = dataBuffer)
540 Utilities.CopyMemory((IntPtr)pDst + bufferOffset, (IntPtr)pSrc + offset, chunk);
544 bufferOffset += chunk;
554 public override unsafe
void Write(IntPtr buffer,
int count)
556 if (!CanWrite)
throw NotSupported(
"Write");
561 if (dataBuffer == null)
563 dataBuffer =
new byte[blockSize];
564 bufferLength = blockSize;
570 var chunk = Math.Min(
count, bufferLength - bufferOffset);
573 fixed (byte* pDst = dataBuffer)
575 Utilities.CopyMemory((IntPtr)pDst + bufferOffset, (IntPtr)buffer + offset, chunk);
579 bufferOffset += chunk;
593 innerStreamPosition = 0;
601 protected override void Dispose(
bool disposing)
604 if (disposeInnerStream)
605 innerStream.Dispose();
606 base.Dispose(disposing);
override unsafe int Read(byte[] buffer, int offset, int count)
When overridden in a derived class, reads a sequence of bytes from the current stream and advances th...
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
override unsafe void Write(byte[] buffer, int offset, int count)
When overridden in a derived class, writes a sequence of bytes to the current stream and advances the...
override void WriteByte(byte value)
Writes a byte to the current position in the stream and advances the position within the stream by on...
override unsafe void Write(IntPtr buffer, int count)
Writes a block of bytes to this stream using data from a buffer. The buffer containing data to write ...
override void Flush()
When overridden in a derived class, clears all buffers for this stream and causes any buffered data t...
override int ReadByte()
Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
Flags
Enumeration of the new Assimp's flags.
ChunkFlags
Flags of a chunk. Please note, this
Block compression stream. Allows to use LZ4 for stream compression.
override void Dispose(bool disposing)
Releases the unmanaged resources used by the T:System.IO.Stream and optionally releases the managed r...
unsafe override int Read(IntPtr buffer, int count)
Reads a block of bytes from the stream and writes the data in a given buffer. When this method return...
HRESULT Decompress(_In_ const Image &cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage &image)
Compression
Compression method enumeration
A Stream with additional methods for native read and write operations using IntPtr.
HRESULT Compress(_In_ const Image &srcImage, _In_ DXGI_FORMAT format, _In_ DWORD compress, _In_ float alphaRef, _Out_ ScratchImage &cImage)
override long Seek(long offset, SeekOrigin origin)
When overridden in a derived class, sets the position within the current stream.
override void SetLength(long value)
When overridden in a derived class, sets the length of the current stream.
LZ4Stream(Stream innerStream, CompressionMode compressionMode, bool highCompression=false, long uncompressedSize=-1, long compressedSize=-1, bool disposeInnerStream=false, int blockSize=1024 *1024)
Initializes a new instance of the LZ4Stream class.
void Reset()
Reset the stream to its initial position and state