/* Copyright 2010-present 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 contiguous byte array. /// public sealed class ByteArrayBuffer : IByteBuffer { // private fields private byte[] _bytes; private bool _disposed; private bool _isReadOnly; private int _length; // constructors /// /// Initializes a new instance of the class. /// /// The bytes. /// Whether the buffer is read only. public ByteArrayBuffer(byte[] bytes, bool isReadOnly = false) : this(bytes, bytes == null ? 0 : bytes.Length, isReadOnly) { } /// /// Initializes a new instance of the class. /// /// The bytes. /// The length. /// Whether the buffer is read only. public ByteArrayBuffer(byte[] bytes, int length, bool isReadOnly = false) { if (bytes == null) { throw new ArgumentNullException("bytes"); } if (length < 0 || length > bytes.Length) { throw new ArgumentOutOfRangeException("length"); } _length = length; _bytes = bytes; _isReadOnly = isReadOnly; } // public properties /// public int Capacity { get { ThrowIfDisposed(); return _isReadOnly ? _length : _bytes.Length; } } /// public bool IsReadOnly { get { ThrowIfDisposed(); return _isReadOnly; } } /// public int Length { get { ThrowIfDisposed(); return _length; } set { ThrowIfDisposed(); if (value < 0 || value > _bytes.Length) { throw new ArgumentOutOfRangeException("value"); } EnsureIsWritable(); _length = value; } } // public methods /// public ArraySegment AccessBackingBytes(int position) { ThrowIfDisposed(); if (position < 0 || position > _length) { throw new ArgumentOutOfRangeException("position"); } return new ArraySegment(_bytes, 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(); Array.Clear(_bytes, position, count); } /// public void Dispose() { _disposed = true; GC.SuppressFinalize(this); } /// public void EnsureCapacity(int minimumCapacity) { if (minimumCapacity < 0) { throw new ArgumentOutOfRangeException("minimumCapacity"); } ThrowIfDisposed(); EnsureIsWritable(); if (minimumCapacity > _bytes.Length) { var powerOf2 = Math.Max(32, PowerOf2.RoundUpToPowerOf2(minimumCapacity)); SetCapacity(powerOf2); } } /// public byte GetByte(int position) { ThrowIfDisposed(); if (position < 0 || position > _length) { throw new ArgumentOutOfRangeException("position"); } return _bytes[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"); } Buffer.BlockCopy(_bytes, 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 ByteArrayBuffer(_bytes, _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(); _bytes[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(); Buffer.BlockCopy(source, offset, _bytes, position, count); } // private methods private void EnsureIsReadOnly() { if (!_isReadOnly) { var message = string.Format("{0} is not read only.", GetType().Name); throw new InvalidOperationException(message); } } private void EnsureIsWritable() { if (_isReadOnly) { var message = string.Format("{0} is not writable.", GetType().Name); throw new InvalidOperationException(message); } } private void SetCapacity(int capacity) { var oldBytes = _bytes; _bytes = new byte[capacity]; var bytesToCopy = capacity < oldBytes.Length ? capacity : oldBytes.Length; Buffer.BlockCopy(oldBytes, 0, _bytes, 0, bytesToCopy); } private void ThrowIfDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().Name); } } } }