Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tusmester/65bcbc430cf449fd7c7092e170c493c9 to your computer and use it in GitHub Desktop.
Save tusmester/65bcbc430cf449fd7c7092e170c493c9 to your computer and use it in GitHub Desktop.
#sn #blob #storage #stream
using System;
using System.IO;
namespace SenseNet.ContentRepository.Tests.Data
{
internal class FileSystemChunkWriterStream : Stream
{
private readonly Guid _id;
private readonly int _chunkSize;
private int _currentChunkIndex;
private int _currentChunkPosition;
private byte[] _buffer;
private bool _flushIsNecessary;
public FileSystemChunkWriterStream(LocalDiskChunkBlobProvider.LocalDiskChunkBlobProviderData providerData, long fullSize)
{
Length = fullSize;
_chunkSize = providerData.ChunkSize;
_id = providerData.Id;
}
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length { get; }
private long __position;
public override long Position
{
get { return __position; }
set
{
// set Position value only through the private SetPosition method
throw new NotSupportedException();
}
}
private void SetPosition(long position)
{
_currentChunkIndex = (position / _chunkSize).ToInt();
_currentChunkPosition = (position % _chunkSize).ToInt();
__position = position;
}
public override void Flush()
{
// nothing to write to the db
if (!_flushIsNecessary)
return;
var bytesToWrite = _currentChunkPosition;
var chunkIndex = _currentChunkIndex;
// If the current chunk position is 0, that means we are at the beginning of the
// next chunk, so we have to write all bytes (chunk size) from the buffer using
// the previous chunk index.
if (_currentChunkPosition == 0)
{
bytesToWrite = _chunkSize;
chunkIndex = _currentChunkIndex - 1;
}
byte[] bytes;
if (bytesToWrite == _buffer.Length)
{
bytes = _buffer;
}
else
{
bytes = new byte[bytesToWrite];
Array.ConstrainedCopy(_buffer, 0, bytes, 0, bytesToWrite);
}
LocalDiskChunkBlobProvider.WriteChunk(_id, chunkIndex, bytes);
_flushIsNecessary = false;
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
if (offset < 0 || count < 0 || buffer.Length < offset + count)
throw new ArgumentException(string.Format("Invalid write parameters: buffer length {0}, offset {1}, count {2}.",
buffer.Length, offset, count));
// nothing to write
if (count == 0)
return;
if (Position >= Length)
throw new InvalidOperationException("Stream length exceeded.");
// Initialize buffer here and not in the constructor
// to allocate memory only when it is needed.
if (_buffer == null)
_buffer = new byte[_chunkSize];
var bytesToWrite = count;
while (bytesToWrite > 0)
{
// if the inner buffer is already full, write it to the db
if (_currentChunkPosition >= _chunkSize || _currentChunkPosition == 0 && _flushIsNecessary)
{
Flush();
if (_currentChunkPosition >= _chunkSize)
{
// reset inner buffer position and move to the next chunk index
_currentChunkPosition = 0;
_currentChunkIndex++;
}
}
// we can only write so much bytes in one round as many slots are left in the inner buffer
var maxBytesToWrite = Math.Min(bytesToWrite, _chunkSize - _currentChunkPosition);
Array.ConstrainedCopy(buffer, offset, _buffer, _currentChunkPosition, maxBytesToWrite);
bytesToWrite -= maxBytesToWrite;
offset += maxBytesToWrite;
_currentChunkPosition += maxBytesToWrite;
_flushIsNecessary = true;
}
SetPosition(Position + count);
}
protected override void Dispose(bool disposing)
{
try
{
this.Flush();
}
finally
{
base.Dispose(disposing);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment