/* Copyright 2010-2015 MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace MongoDB.Bson.IO
{
///
/// An IByteBuffer that is backed by a single chunk.
///
public sealed class SingleChunkBuffer : IByteBuffer
{
// private fields
private IBsonChunk _chunk;
private bool _disposed;
private bool _isReadOnly;
private int _length;
// constructors
///
/// Initializes a new instance of the class.
///
/// The chuns.
/// The length.
/// Whether the buffer is read only.
public SingleChunkBuffer(IBsonChunk chunk, int length, bool isReadOnly = false)
{
if (chunk == null)
{
throw new ArgumentNullException("chunk");
}
if (length < 0 || length > chunk.Bytes.Count)
{
throw new ArgumentOutOfRangeException("length");
}
_chunk = chunk;
_length = length;
_isReadOnly = isReadOnly;
}
// public properties
///
public int Capacity
{
get
{
ThrowIfDisposed();
return _isReadOnly ? _length : _chunk.Bytes.Count;
}
}
///
public bool IsReadOnly
{
get
{
ThrowIfDisposed();
return _isReadOnly;
}
}
///
public int Length
{
get
{
ThrowIfDisposed();
return _length;
}
set
{
ThrowIfDisposed();
if (value < 0 || value > _chunk.Bytes.Count)
{
throw new ArgumentOutOfRangeException("value");
}
EnsureIsWritable();
_length = value;
}
}
// public methods
///
public ArraySegment AccessBackingBytes(int position)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
var segment = _chunk.Bytes;
return new ArraySegment(segment.Array, segment.Offset + position, _length - position);
}
///
public void Clear(int position, int count)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
if (count < 0 || position + count > _length)
{
throw new ArgumentOutOfRangeException("count");
}
EnsureIsWritable();
var segment = _chunk.Bytes;
Array.Clear(segment.Array, segment.Offset + position, count);
}
///
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_chunk.Dispose();
_chunk = null;
}
}
///
public void EnsureCapacity(int minimumCapacity)
{
if (minimumCapacity < 0)
{
throw new ArgumentOutOfRangeException("minimumCapacity");
}
ThrowIfDisposed();
EnsureIsWritable();
if (_chunk.Bytes.Count < minimumCapacity)
{
throw new NotSupportedException("Capacity cannot be expanded for a SingleChunkBuffer.");
}
}
///
public byte GetByte(int position)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
var segment = _chunk.Bytes;
return segment.Array[segment.Offset + position];
}
///
public void GetBytes(int position, byte[] destination, int offset, int count)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
if (destination == null)
{
throw new ArgumentNullException("destination");
}
if (offset < 0 || offset > destination.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || position + count > _length || offset + count > destination.Length)
{
throw new ArgumentOutOfRangeException("count");
}
var segment = _chunk.Bytes;
Buffer.BlockCopy(segment.Array, segment.Offset + position, destination, offset, count);
}
///
public IByteBuffer GetSlice(int position, int length)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
if (length < 0 || position + length > _length)
{
throw new ArgumentOutOfRangeException("length");
}
EnsureIsReadOnly();
var forkedBuffer = new SingleChunkBuffer(_chunk.Fork(), _length, isReadOnly: true);
return new ByteBufferSlice(forkedBuffer, position, length);
}
///
public void MakeReadOnly()
{
ThrowIfDisposed();
_isReadOnly = true;
}
///
public void SetByte(int position, byte value)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
EnsureIsWritable();
var segment = _chunk.Bytes;
segment.Array[segment.Offset + position] = value;
}
///
public void SetBytes(int position, byte[] source, int offset, int count)
{
ThrowIfDisposed();
if (position < 0 || position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
if (source == null)
{
throw new ArgumentNullException("source");
}
if (offset < 0 || offset > source.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || position + count > _length || offset + count > source.Length)
{
throw new ArgumentOutOfRangeException("count");
}
EnsureIsWritable();
var segment = _chunk.Bytes;
Buffer.BlockCopy(source, offset, segment.Array, segment.Offset + position, count);
}
// private methods
private void EnsureIsReadOnly()
{
if (!_isReadOnly)
{
throw new InvalidOperationException("MultiChunkBuffer is not read only.");
}
}
private void EnsureIsWritable()
{
if (_isReadOnly)
{
throw new InvalidOperationException("MultiChunkBuffer is not writable.");
}
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
}
}