Browse files

CSHARP-666: Initial implementation of lazy and raw BsonDocument and B…

…sonArray.
  • Loading branch information...
1 parent 25882b0 commit a44149564d2a50fac370cbbd37afa8655929f65d rstam committed Feb 10, 2013
Showing with 6,244 additions and 867 deletions.
  1. +83 −7 MongoDB.Bson/IO/BsonBinaryReader.cs
  2. +79 −7 MongoDB.Bson/IO/BsonBinaryWriter.cs
  3. +275 −555 MongoDB.Bson/IO/BsonBuffer.cs
  4. +105 −0 MongoDB.Bson/IO/BsonChunk.cs
  5. +128 −0 MongoDB.Bson/IO/BsonChunkPool.cs
  6. +1 −0 MongoDB.Bson/IO/BsonDocumentWriter.cs
  7. +52 −4 MongoDB.Bson/IO/BsonReader.cs
  8. +56 −2 MongoDB.Bson/IO/BsonWriter.cs
  9. +533 −0 MongoDB.Bson/IO/ByteArrayBuffer.cs
  10. +94 −0 MongoDB.Bson/IO/ByteBufferFactory.cs
  11. +162 −0 MongoDB.Bson/IO/IByteBuffer.cs
  12. +1 −0 MongoDB.Bson/IO/JsonWriter.cs
  13. +720 −0 MongoDB.Bson/IO/MultiChunkBuffer.cs
  14. +118 −0 MongoDB.Bson/IO/SingleChunkBuffer.cs
  15. +19 −1 MongoDB.Bson/MongoDB.Bson.csproj
  16. +62 −50 MongoDB.Bson/ObjectModel/BsonArray.cs
  17. +79 −67 MongoDB.Bson/ObjectModel/BsonDocument.cs
  18. +562 −0 MongoDB.Bson/ObjectModel/LazyBsonArray.cs
  19. +830 −0 MongoDB.Bson/ObjectModel/LazyBsonDocument.cs
  20. +604 −0 MongoDB.Bson/ObjectModel/RawBsonArray.cs
  21. +890 −0 MongoDB.Bson/ObjectModel/RawBsonDocument.cs
  22. +91 −0 MongoDB.Bson/Serialization/Serializers/LazyBsonArraySerializer.cs
  23. +91 −0 MongoDB.Bson/Serialization/Serializers/LazyBsonDocumentSerializer.cs
  24. +83 −0 MongoDB.Bson/Serialization/Serializers/RawBsonArraySerializer.cs
  25. +83 −0 MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs
  26. +6 −6 MongoDB.Driver/Communication/Messages/MongoDeleteMessage.cs
  27. +7 −6 MongoDB.Driver/Communication/Messages/MongoGetMoreMessage.cs
  28. +19 −20 MongoDB.Driver/Communication/Messages/MongoInsertMessage.cs
  29. +6 −6 MongoDB.Driver/Communication/Messages/MongoKillCursorsMessage.cs
  30. +7 −20 MongoDB.Driver/Communication/Messages/MongoQueryMessage.cs
  31. +29 −10 MongoDB.Driver/Communication/Messages/MongoReplyMessage.cs
  32. +10 −44 MongoDB.Driver/Communication/Messages/MongoRequestMessage.cs
  33. +6 −6 MongoDB.Driver/Communication/Messages/MongoUpdateMessage.cs
  34. +35 −27 MongoDB.Driver/Communication/MongoConnection.cs
  35. +14 −17 MongoDB.Driver/MongoCollection.cs
  36. +6 −12 MongoDB.Driver/MongoCursorEnumerator.cs
  37. +9 −0 MongoDB.Driver/MongoDB.Driver.csproj
  38. +116 −0 MongoDB.Shared/CanonicalDisposableClass.cs
  39. +82 −0 MongoDB.Shared/CanonicalDisposableDerivedClass.cs
  40. +91 −0 MongoDB.Shared/CanonicalDisposableStruct.cs
View
90 MongoDB.Bson/IO/BsonBinaryReader.cs
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+using System;
using System.IO;
namespace MongoDB.Bson.IO
@@ -35,18 +36,35 @@ public class BsonBinaryReader : BsonReader
/// <param name="buffer">A BsonBuffer.</param>
/// <param name="settings">A BsonBinaryReaderSettings.</param>
public BsonBinaryReader(BsonBuffer buffer, BsonBinaryReaderSettings settings)
+ : this(buffer ?? new BsonBuffer(), buffer == null, settings)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the BsonBinaryReader class.
+ /// </summary>
+ /// <param name="buffer">A BsonBuffer.</param>
+ /// <param name="disposeBuffer">if set to <c>true</c> this BsonBinaryReader will own the buffer and when Dispose is called the buffer will be Disposed also.</param>
+ /// <param name="settings">A BsonBinaryReaderSettings.</param>
+ /// <exception cref="System.ArgumentNullException">
+ /// buffer
+ /// or
+ /// settings
+ /// </exception>
+ public BsonBinaryReader(BsonBuffer buffer, bool disposeBuffer, BsonBinaryReaderSettings settings)
: base(settings)
{
if (buffer == null)
{
- _buffer = new BsonBuffer();
- _disposeBuffer = true; // only call Dispose if we allocated the buffer
+ throw new ArgumentNullException("buffer");
}
- else
+ if (settings == null)
{
- _buffer = buffer;
- _disposeBuffer = false;
+ throw new ArgumentNullException("settings");
}
+
+ _buffer = buffer;
+ _disposeBuffer = disposeBuffer;
_binaryReaderSettings = settings; // already frozen by base class
_context = new BsonBinaryReaderContext(null, ContextType.TopLevel, 0, 0);
}
@@ -316,7 +334,7 @@ public override void ReadEndDocument()
}
_context = _context.PopContext(_buffer.Position);
- if (_context != null && _context.ContextType == ContextType.JavaScriptWithScope)
+ if (_context.ContextType == ContextType.JavaScriptWithScope)
{
_context = _context.PopContext(_buffer.Position); // JavaScriptWithScope
}
@@ -426,6 +444,64 @@ public override ObjectId ReadObjectId()
}
/// <summary>
+ /// Reads a raw BSON array.
+ /// </summary>
+ /// <returns>
+ /// The raw BSON array.
+ /// </returns>
+ public override IByteBuffer ReadRawBsonArray()
+ {
+ if (Disposed) { ThrowObjectDisposedException(); }
+ VerifyBsonType("ReadStartArray", BsonType.Array);
+
+ var position = _buffer.Position;
+ var length = _buffer.ReadInt32();
+ var slice = _buffer.ByteBuffer.GetSlice(position, length);
+ _buffer.Position = position + length;
+
+ switch (_context.ContextType)
+ {
+ case ContextType.Array: State = BsonReaderState.Type; break;
+ case ContextType.Document: State = BsonReaderState.Type; break;
+ case ContextType.TopLevel: State = BsonReaderState.Done; break;
+ default: throw new BsonInternalException("Unexpected ContextType.");
+ }
+
+ return slice;
+ }
+
+ /// <summary>
+ /// Reads a raw BSON document.
+ /// </summary>
+ /// <returns>
+ /// The raw BSON document.
+ /// </returns>
+ public override IByteBuffer ReadRawBsonDocument()
+ {
+ if (Disposed) { ThrowObjectDisposedException(); }
+ VerifyBsonType("ReadRawBsonDocument", BsonType.Document);
+
+ var position = _buffer.Position;
+ var length = _buffer.ReadInt32();
+ var slice = _buffer.ByteBuffer.GetSlice(position, length);
+ _buffer.Position = position + length;
+
+ if (_context.ContextType == ContextType.JavaScriptWithScope)
+ {
+ _context = _context.PopContext(_buffer.Position); // JavaScriptWithScope
+ }
+ switch (_context.ContextType)
+ {
+ case ContextType.Array: State = BsonReaderState.Type; break;
+ case ContextType.Document: State = BsonReaderState.Type; break;
+ case ContextType.TopLevel: State = BsonReaderState.Done; break;
+ default: throw new BsonInternalException("Unexpected ContextType.");
+ }
+
+ return slice;
+ }
+
+ /// <summary>
/// Reads a BSON regular expression from the reader.
/// </summary>
/// <returns>A BsonRegularExpression.</returns>
@@ -597,8 +673,8 @@ protected override void Dispose(bool disposing)
if (_disposeBuffer)
{
_buffer.Dispose();
- _buffer = null;
}
+ _buffer = null;
}
catch { } // ignore exceptions
}
View
86 MongoDB.Bson/IO/BsonBinaryWriter.cs
@@ -38,19 +38,36 @@ public class BsonBinaryWriter : BsonWriter
/// <param name="buffer">A BsonBuffer.</param>
/// <param name="settings">Optional BsonBinaryWriter settings.</param>
public BsonBinaryWriter(Stream stream, BsonBuffer buffer, BsonBinaryWriterSettings settings)
- : base(settings)
+ : this(buffer ?? new BsonBuffer(), buffer == null, settings)
{
_stream = stream;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the BsonBinaryWriter class.
+ /// </summary>
+ /// <param name="buffer">A BsonBuffer.</param>
+ /// <param name="disposeBuffer">if set to <c>true</c> this BsonBinaryReader will own the buffer and when Dispose is called the buffer will be Disposed also.</param>
+ /// <param name="settings">Optional BsonBinaryWriter settings.</param>
+ /// <exception cref="System.ArgumentNullException">
+ /// encoder
+ /// or
+ /// settings
+ /// </exception>
+ public BsonBinaryWriter(BsonBuffer buffer, bool disposeBuffer, BsonBinaryWriterSettings settings)
+ : base(settings)
+ {
if (buffer == null)
{
- _buffer = new BsonBuffer();
- _disposeBuffer = true; // only call Dispose if we allocated the buffer
+ throw new ArgumentNullException("encoder");
}
- else
+ if (settings == null)
{
- _buffer = buffer;
- _disposeBuffer = false;
+ throw new ArgumentNullException("settings");
}
+
+ _buffer = buffer;
+ _disposeBuffer = disposeBuffer;
_binaryWriterSettings = settings; // already frozen by base class
_context = null;
@@ -462,6 +479,60 @@ public override void WriteObjectId(ObjectId objectId)
}
/// <summary>
+ /// Writes a raw BSON array.
+ /// </summary>
+ /// <param name="slice">The byte buffer containing the raw BSON array.</param>
+ /// <exception cref="System.NotImplementedException"></exception>
+ public override void WriteRawBsonArray(IByteBuffer slice)
+ {
+ if (Disposed) { throw new ObjectDisposedException("BsonBinaryWriter"); }
+ if (State != BsonWriterState.Value)
+ {
+ ThrowInvalidState("WriteRawBsonArray", BsonWriterState.Value);
+ }
+
+ _buffer.WriteByte((byte)BsonType.Array);
+ WriteNameHelper();
+ _buffer.ByteBuffer.WriteBytes(slice); // assumes byteBuffer is a valid raw BSON document
+
+ State = GetNextState();
+ }
+
+ /// <summary>
+ /// Writes a raw BSON document.
+ /// </summary>
+ /// <param name="slice">The byte buffer containing the raw BSON document.</param>
+ public override void WriteRawBsonDocument(IByteBuffer slice)
+ {
+ if (Disposed) { throw new ObjectDisposedException("BsonBinaryWriter"); }
+ if (State != BsonWriterState.Initial && State != BsonWriterState.Value && State != BsonWriterState.ScopeDocument && State != BsonWriterState.Done)
+ {
+ ThrowInvalidState("WriteRawBsonDocument", BsonWriterState.Initial, BsonWriterState.Value, BsonWriterState.ScopeDocument, BsonWriterState.Done);
+ }
+
+ if (State == BsonWriterState.Value)
+ {
+ _buffer.WriteByte((byte)BsonType.Document);
+ WriteNameHelper();
+ }
+ _buffer.ByteBuffer.WriteBytes(slice); // assumes byteBuffer is a valid raw BSON document
+
+ if (_context == null)
+ {
+ State = BsonWriterState.Done;
+ }
+ else
+ {
+ if (_context.ContextType == ContextType.JavaScriptWithScope)
+ {
+ BackpatchSize(); // size of the JavaScript with scope value
+ _context = _context.ParentContext;
+ }
+ State = GetNextState();
+ }
+ }
+
+ /// <summary>
/// Writes a BSON regular expression to the writer.
/// </summary>
/// <param name="regex">A BsonRegularExpression.</param>
@@ -612,9 +683,10 @@ protected override void Dispose(bool disposing)
if (_disposeBuffer)
{
_buffer.Dispose();
- _buffer = null;
}
+ _buffer = null;
}
+ base.Dispose(disposing);
}
// private methods
View
830 MongoDB.Bson/IO/BsonBuffer.cs
@@ -14,7 +14,6 @@
*/
using System;
-using System.Collections.Generic;
using System.IO;
using System.Text;
@@ -26,22 +25,14 @@ namespace MongoDB.Bson.IO
public class BsonBuffer : IDisposable
{
// private static fields
- private static Stack<byte[]> __chunkPool = new Stack<byte[]>();
- private static int __maxChunkPoolSize = 64;
- private const int __chunkSize = 16 * 1024; // 16KiB
private static readonly string[] __asciiStringTable = BuildAsciiStringTable();
private static readonly UTF8Encoding __utf8Encoding = new UTF8Encoding(false, true); // throw on invalid bytes
private static readonly bool[] __validBsonTypes = new bool[256];
// private fields
private bool _disposed = false;
- private List<byte[]> _chunks = new List<byte[]>(4);
- private int _capacity;
- private int _length; // always use property to set so position can be adjusted if necessary
- private int _position; // always use property to set so chunkIndex/chunkOffset/chunk/length will be set also
- private int _chunkIndex;
- private int _chunkOffset;
- private byte[] _chunk;
+ private IByteBuffer _byteBuffer;
+ private bool _disposeByteBuffer;
// static constructor
static BsonBuffer()
@@ -57,52 +48,47 @@ static BsonBuffer()
/// Initializes a new instance of the BsonBuffer class.
/// </summary>
public BsonBuffer()
+ : this(new MultiChunkBuffer(BsonChunkPool.Default), true)
{
- // let EnsureAvailable get the first chunk
}
- // public static properties
/// <summary>
- /// Gets or sets the max chunk pool size.
+ /// Initializes a new instance of the <see cref="BsonBuffer" /> class.
/// </summary>
- public static int MaxChunkPoolSize
+ /// <param name="byteBuffer">The buffer.</param>
+ /// <param name="disposeByteBuffer">if set to <c>true</c> this BsonBuffer will own the byte buffer and when Dispose is called the byte buffer will be Disposed also.</param>
+ public BsonBuffer(IByteBuffer byteBuffer, bool disposeByteBuffer)
{
- get
- {
- lock (__chunkPool)
- {
- return __maxChunkPoolSize;
- }
- }
- set
- {
- lock (__chunkPool)
- {
- __maxChunkPoolSize = value;
- }
- }
+ _byteBuffer = byteBuffer;
+ _disposeByteBuffer = disposeByteBuffer;
}
// public properties
/// <summary>
+ /// Gets the byte buffer.
+ /// </summary>
+ /// <value>
+ /// The byte buffer.
+ /// </value>
+ public IByteBuffer ByteBuffer
+ {
+ get { return _byteBuffer; }
+ }
+
+ /// <summary>
/// Gets or sets the length of the data in the buffer.
/// </summary>
public int Length
{
get
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- return _length;
+ ThrowIfDisposed();
+ return _byteBuffer.Length;
}
set
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(value - _position);
- _length = value;
- if (_position > value)
- {
- Position = value;
- }
+ ThrowIfDisposed();
+ _byteBuffer.Length = value;
}
}
@@ -113,30 +99,13 @@ public int Position
{
get
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- return _position;
+ ThrowIfDisposed();
+ return _byteBuffer.Position;
}
set
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- if (_chunkIndex != value / __chunkSize)
- {
- _chunkIndex = value / __chunkSize;
- if (_chunkIndex < _chunks.Count)
- {
- _chunk = _chunks[_chunkIndex];
- }
- else
- {
- _chunk = null; // EnsureSpaceAvailable will set this when it gets the chunk
- }
- }
- _chunkOffset = value % __chunkSize;
- _position = value;
- if (_length < value)
- {
- _length = value;
- }
+ ThrowIfDisposed();
+ _byteBuffer.Position = value;
}
}
@@ -153,34 +122,6 @@ private static string[] BuildAsciiStringTable()
return asciiStringTable;
}
- private static byte[] GetChunk()
- {
- lock (__chunkPool)
- {
- if (__chunkPool.Count > 0)
- {
- return __chunkPool.Pop();
- }
- }
- return new byte[__chunkSize]; // release the lock before allocating memory
- }
-
- private static bool IsValidBsonType(BsonType bsonType)
- {
- return __validBsonTypes[(byte)bsonType];
- }
-
- private static void ReleaseChunk(byte[] chunk)
- {
- lock (__chunkPool)
- {
- if (__chunkPool.Count < __maxChunkPoolSize)
- {
- __chunkPool.Push(chunk);
- }
- }
- }
-
// public methods
/// <summary>
/// Backpatches the length of an object.
@@ -189,48 +130,20 @@ private static void ReleaseChunk(byte[] chunk)
/// <param name="length">The length of the object.</param>
public void Backpatch(int position, int length)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- // use local chunk variables because we're writing at a different position
- int chunkIndex = position / __chunkSize;
- int chunkOffset = position % __chunkSize;
- var chunk = _chunks[chunkIndex];
- if (__chunkSize - chunkOffset >= 4)
- {
- chunk[chunkOffset + 0] = (byte)(length);
- chunk[chunkOffset + 1] = (byte)(length >> 8);
- chunk[chunkOffset + 2] = (byte)(length >> 16);
- chunk[chunkOffset + 3] = (byte)(length >> 24);
- }
- else
- {
- // straddles chunk boundary
- for (int i = 0; i < 4; i++)
- {
- chunk[chunkOffset++] = (byte)length;
- if (chunkOffset == __chunkSize)
- {
- chunk = _chunks[++chunkIndex];
- chunkOffset = 0;
- }
- length >>= 8;
- }
- }
+ ThrowIfDisposed();
+ var savedPosition = _byteBuffer.Position;
+ _byteBuffer.Position = position;
+ WriteInt32(length);
+ _byteBuffer.Position = savedPosition;
}
/// <summary>
/// Clears the data in the buffer.
/// </summary>
public void Clear()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- Position = 0;
- foreach (var localChunk in _chunks)
- {
- ReleaseChunk(localChunk);
- }
- _chunks.Clear();
- _chunk = null;
- _capacity = 0;
+ ThrowIfDisposed();
+ _byteBuffer.Clear();
}
/// <summary>
@@ -240,34 +153,23 @@ public void Clear()
/// <param name="destination">The destination byte array.</param>
/// <param name="destinationOffset">The destination offset in the byte array.</param>
/// <param name="count">The number of bytes to copy.</param>
+ [Obsolete("Use ReadBytes instead.")]
public void CopyTo(int sourceOffset, byte[] destination, int destinationOffset, int count)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- var chunkIndex = sourceOffset / __chunkSize;
- var chunkOffset = sourceOffset % __chunkSize;
- while (count > 0)
- {
- var chunk = _chunks[chunkIndex];
- var available = __chunkSize - chunkOffset;
- var partialCount = (count > available) ? available : count;
- Buffer.BlockCopy(chunk, chunkOffset, destination, destinationOffset, partialCount);
- chunkIndex++;
- chunkOffset = 0;
- destinationOffset += partialCount;
- count -= partialCount;
- }
+ ThrowIfDisposed();
+ var savedPosition = _byteBuffer.Position;
+ _byteBuffer.Position = sourceOffset;
+ _byteBuffer.ReadBytes(destination, destinationOffset, count);
+ _byteBuffer.Position = savedPosition;
}
/// <summary>
/// Disposes of any resources held by the buffer.
/// </summary>
public void Dispose()
{
- if (!_disposed)
- {
- Clear();
- _disposed = true;
- }
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
@@ -289,64 +191,34 @@ public void LoadFrom(Stream stream)
/// <param name="count">The number of bytes to load.</param>
public void LoadFrom(Stream stream, int count)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
-
- EnsureSpaceAvailable(count);
- var localChunkIndex = _chunkIndex;
- var localChunkOffset = _chunkOffset;
- while (count > 0)
- {
- var localChunk = _chunks[localChunkIndex];
- int available = __chunkSize - localChunkOffset;
- int partialCount = (count > available) ? available : count;
- // might take several reads, you never know with a network stream.
- int bytesPending = partialCount;
- while (bytesPending > 0)
- {
- var bytesRead = stream.Read(localChunk, localChunkOffset, bytesPending);
- if (bytesRead == 0)
- {
- throw new EndOfStreamException();
- }
- else
- {
- localChunkOffset += bytesRead;
- bytesPending -= bytesRead;
- }
- }
- localChunkIndex++;
- localChunkOffset = 0;
- count -= partialCount;
- _length += partialCount;
- }
+ ThrowIfDisposed();
+ _byteBuffer.LoadFrom(stream, count); // does not advance position
}
/// <summary>
/// Peeks at the next byte in the buffer and returns it as a BsonType.
/// </summary>
/// <returns>A BsonType.</returns>
+ [Obsolete("Use ReadBsonType instead.")]
public BsonType PeekBsonType()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(1);
- var bsonType = (BsonType)_chunk[_chunkOffset];
- if (!IsValidBsonType(bsonType))
- {
- string message = string.Format("Invalid BsonType {0}.", (int)bsonType);
- throw new FileFormatException(message);
- }
- return bsonType;
+ ThrowIfDisposed();
+ var value = ReadBsonType();
+ Position -= 1;
+ return value;
}
/// <summary>
/// Peeks at the next byte in the buffer.
/// </summary>
/// <returns>A Byte.</returns>
+ [Obsolete("Use ReadByte instead.")]
public byte PeekByte()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(1);
- return _chunk[_chunkOffset];
+ ThrowIfDisposed();
+ var value = ReadByte();
+ Position -= 1;
+ return value;
}
/// <summary>
@@ -355,11 +227,8 @@ public byte PeekByte()
/// <returns>A Boolean.</returns>
public bool ReadBoolean()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(1);
- var value = _chunk[_chunkOffset] != 0;
- Position++;
- return value;
+ ThrowIfDisposed();
+ return _byteBuffer.ReadByte() != 0;
}
/// <summary>
@@ -368,16 +237,14 @@ public bool ReadBoolean()
/// <returns>A BsonType.</returns>
public BsonType ReadBsonType()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(1);
- var bsonType = (BsonType)_chunk[_chunkOffset];
- if (!IsValidBsonType(bsonType))
+ ThrowIfDisposed();
+ var bsonType = (int)_byteBuffer.ReadByte();
+ if (!__validBsonTypes[bsonType])
{
- string message = string.Format("Invalid BsonType {0}.", (int)bsonType);
+ string message = string.Format("Invalid BsonType {0}.", bsonType);
throw new FileFormatException(message);
}
- Position++;
- return bsonType;
+ return (BsonType)bsonType;
}
/// <summary>
@@ -386,11 +253,8 @@ public BsonType ReadBsonType()
/// <returns>A Byte.</returns>
public byte ReadByte()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(1);
- var value = _chunk[_chunkOffset];
- Position++;
- return value;
+ ThrowIfDisposed();
+ return _byteBuffer.ReadByte();
}
/// <summary>
@@ -400,21 +264,7 @@ public byte ReadByte()
/// <returns>A byte array.</returns>
public byte[] ReadBytes(int count)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(count);
- var value = new byte[count];
- int destinationOffset = 0;
- while (count > 0)
- {
- // might only be reading part of the first or last chunk
- int available = __chunkSize - _chunkOffset;
- int partialCount = (count > available) ? available : count;
- Buffer.BlockCopy(_chunk, _chunkOffset, value, destinationOffset, partialCount);
- Position += partialCount;
- destinationOffset += partialCount;
- count -= partialCount;
- }
- return value;
+ return _byteBuffer.ReadBytes(count);
}
/// <summary>
@@ -423,17 +273,17 @@ public byte[] ReadBytes(int count)
/// <returns>A Double.</returns>
public double ReadDouble()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(8);
- if (__chunkSize - _chunkOffset >= 8)
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.ReadBackingBytes(8);
+ if (segment.Count >= 8)
{
- var value = BitConverter.ToDouble(_chunk, _chunkOffset);
- Position += 8;
- return value;
+ return BitConverter.ToDouble(segment.Array, segment.Offset);
}
else
{
- return BitConverter.ToDouble(ReadBytes(8), 0); // straddles chunk boundary
+ var bytes = _byteBuffer.ReadBytes(8);
+ return BitConverter.ToDouble(bytes, 0);
}
}
@@ -443,22 +293,22 @@ public double ReadDouble()
/// <returns>An Int32.</returns>
public int ReadInt32()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(4);
- if (__chunkSize - _chunkOffset >= 4)
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.ReadBackingBytes(4);
+ if (segment.Count >= 4)
{
// for int only we come out ahead with this code vs using BitConverter
- var value =
- ((int)_chunk[_chunkOffset + 0]) +
- ((int)_chunk[_chunkOffset + 1] << 8) +
- ((int)_chunk[_chunkOffset + 2] << 16) +
- ((int)_chunk[_chunkOffset + 3] << 24);
- Position += 4;
- return value;
+ return
+ ((int)segment.Array[segment.Offset + 0]) +
+ ((int)segment.Array[segment.Offset + 1] << 8) +
+ ((int)segment.Array[segment.Offset + 2] << 16) +
+ ((int)segment.Array[segment.Offset + 3] << 24);
}
else
{
- return BitConverter.ToInt32(ReadBytes(4), 0); // straddles chunk boundary
+ var bytes = _byteBuffer.ReadBytes(4);
+ return BitConverter.ToInt32(bytes, 0);
}
}
@@ -468,17 +318,17 @@ public int ReadInt32()
/// <returns>An Int64.</returns>
public long ReadInt64()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(8);
- if (__chunkSize - _chunkOffset >= 8)
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.ReadBackingBytes(8);
+ if (segment.Count >= 8)
{
- var value = BitConverter.ToInt64(_chunk, _chunkOffset);
- Position += 8;
- return value;
+ return BitConverter.ToInt64(segment.Array, segment.Offset);
}
else
{
- return BitConverter.ToInt64(ReadBytes(8), 0); // straddles chunk boundary
+ var bytes = _byteBuffer.ReadBytes(8);
+ return BitConverter.ToInt64(bytes, 0);
}
}
@@ -488,22 +338,22 @@ public long ReadInt64()
/// <returns>An ObjectId.</returns>
public ObjectId ReadObjectId()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureDataAvailable(12);
- if (__chunkSize - _chunkOffset >= 12)
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.ReadBackingBytes(12);
+ if (segment.Count >= 12)
{
- var c = _chunk;
- var o = _chunkOffset;
- var timestamp = (c[o + 0] << 24) + (c[o + 1] << 16) + (c[o + 2] << 8) + c[o + 3];
- var machine = (c[o + 4] << 16) + (c[o + 5] << 8) + c[o + 6];
- var pid = (short)((c[o + 7] << 8) + c[o + 8]);
- var increment = (c[o + 9] << 16) + (c[o + 10] << 8) + c[o + 11];
- Position += 12;
+ var bytes = segment.Array;
+ var offset = segment.Offset;
+ var timestamp = (bytes[offset + 0] << 24) + (bytes[offset + 1] << 16) + (bytes[offset + 2] << 8) + bytes[offset + 3];
+ var machine = (bytes[offset + 4] << 16) + (bytes[offset + 5] << 8) + bytes[offset + 6];
+ var pid = (short)((bytes[offset + 7] << 8) + bytes[offset + 8]);
+ var increment = (bytes[offset + 9] << 16) + (bytes[offset + 10] << 8) + bytes[offset + 11];
return new ObjectId(timestamp, machine, pid, increment);
}
else
{
- var bytes = ReadBytes(12); // straddles chunk boundary
+ var bytes = _byteBuffer.ReadBytes(12);
return new ObjectId(bytes);
}
}
@@ -531,34 +381,33 @@ public void ReadObjectId(out int timestamp, out int machine, out short pid, out
/// <returns>A String.</returns>
public string ReadString()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
+ ThrowIfDisposed();
var length = ReadInt32(); // length including the null terminator
if (length <= 0)
{
var message = string.Format("Invalid string length: {0} (the length includes the null terminator so it must be greater than or equal to 1).", length);
throw new FileFormatException(message);
}
- EnsureDataAvailable(length);
string value;
- if (__chunkSize - _chunkOffset >= length)
+ byte finalByte;
+
+ var segment = _byteBuffer.ReadBackingBytes(length);
+ if (segment.Count >= length)
{
- if (_chunk[_chunkOffset + length - 1] != 0)
- {
- throw new FileFormatException("String is missing null terminator.");
- }
- value = DecodeUtf8String(_chunk, _chunkOffset, length - 1); // don't decode the null terminator
- Position += length;
+ value = DecodeUtf8String(segment.Array, segment.Offset, length - 1);
+ finalByte = segment.Array[segment.Offset + length - 1];
}
else
{
- // straddles chunk boundary
- var bytes = ReadBytes(length); // read the null terminator also
- if (bytes[length - 1] != 0)
- {
- throw new FileFormatException("String is missing null terminator.");
- }
- value = __utf8Encoding.GetString(bytes, 0, length - 1); // don't decode the null terminator
+ var bytes = _byteBuffer.ReadBytes(length);
+ value = DecodeUtf8String(bytes, 0, length - 1);
+ finalByte = bytes[length - 1];
+ }
+
+ if (finalByte != 0)
+ {
+ throw new FileFormatException("String is missing null terminator.");
}
return value;
@@ -570,9 +419,15 @@ public string ReadString()
/// <returns>A string.</returns>
public string ReadCString()
{
- bool found;
- int value;
- return ReadCString(null, out found, out value);
+ ThrowIfDisposed();
+
+ var nullPosition = _byteBuffer.FindNullByte();
+ if (nullPosition == -1)
+ {
+ throw new BsonSerializationException("Missing null terminator.");
+ }
+
+ return ReadCString(nullPosition);
}
/// <summary>
@@ -585,79 +440,44 @@ public string ReadCString()
/// <returns>A string.</returns>
public string ReadCString<TValue>(BsonTrie<TValue> bsonTrie, out bool found, out TValue value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
+ ThrowIfDisposed();
found = false;
value = default(TValue);
- // optimize for the case where the null terminator is on the same chunk
- int partialCount;
- if (_chunkIndex < _chunks.Count - 1)
- {
- partialCount = __chunkSize - _chunkOffset; // remaining part of any chunk but the last
- }
- else
- {
- partialCount = _length - _position; // populated part of last chunk
- }
- var bsonTrieNode = bsonTrie != null ? bsonTrie.Root : null;
- var index = IndexOfNull(_chunk, _chunkOffset, partialCount, ref bsonTrieNode);
- if (index != -1)
+ if (bsonTrie == null)
{
- var stringLength = index - _chunkOffset;
- string cstring;
- if (bsonTrieNode != null && bsonTrieNode.HasValue)
- {
- cstring = bsonTrieNode.ElementName;
- value = bsonTrieNode.Value;
- found = true;
- }
- else
- {
- cstring = DecodeUtf8String(_chunk, _chunkOffset, stringLength);
- }
- Position += stringLength + 1;
- return cstring;
+ return ReadCString();
}
- // the null terminator is not on the same chunk so keep looking starting with the next chunk
- var localChunkIndex = _chunkIndex + 1;
- var localPosition = localChunkIndex * __chunkSize;
- while (localPosition < _length)
+ var savedPosition = _byteBuffer.Position;
+ var bsonTrieNode = bsonTrie.Root;
+ while (true)
{
- var localChunk = _chunks[localChunkIndex];
- if (localChunkIndex < _chunks.Count - 1)
- {
- partialCount = __chunkSize; // all of any chunk but the last
- }
- else
- {
- partialCount = _length - localPosition; // populated part of last chunk
- }
- index = IndexOfNull(localChunk, 0, partialCount, ref bsonTrieNode);
- if (index != -1)
+ var keyByte = _byteBuffer.ReadByte();
+ if (keyByte == 0)
{
- localPosition += index;
- var stringLength = localPosition - _position;
- string cstring;
- if (bsonTrieNode != null && bsonTrieNode.HasValue)
+ if (bsonTrieNode.HasValue)
{
- cstring = bsonTrieNode.ElementName;
- value = bsonTrieNode.Value;
found = true;
- Position += stringLength + 1;
+ value = bsonTrieNode.Value;
+ return bsonTrieNode.ElementName;
}
else
{
- cstring = __utf8Encoding.GetString(ReadBytes(stringLength)); // ReadBytes advances over string
- Position += 1; // skip over null byte at end
+ var nullPosition = _byteBuffer.Position - 1;
+ _byteBuffer.Position = savedPosition;
+ return ReadCString(nullPosition);
}
- return cstring;
}
- localChunkIndex++;
- localPosition += __chunkSize;
- }
- throw new FileFormatException("String is missing null terminator.");
+ bsonTrieNode = bsonTrieNode.GetChild(keyByte);
+ if (bsonTrieNode == null)
+ {
+ var nullPosition = _byteBuffer.FindNullByte(); // starting from where we got so far
+ _byteBuffer.Position = savedPosition;
+ return ReadCString(nullPosition);
+ }
+ }
}
/// <summary>
@@ -666,62 +486,21 @@ public string ReadCString<TValue>(BsonTrie<TValue> bsonTrie, out bool found, out
/// <param name="count">The number of bytes to skip.</param>
public void Skip(int count)
{
- // TODO: optimize this method
- Position += count;
+ _byteBuffer.Position += count;
}
/// <summary>
/// Skips over a CString in the buffer (advances the position).
/// </summary>
public void SkipCString()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
-
- // optimize for the case where the null terminator is on the same chunk
- int partialCount;
- if (_chunkIndex < _chunks.Count - 1)
- {
- partialCount = __chunkSize - _chunkOffset; // remaining part of any chunk but the last
- }
- else
- {
- partialCount = _length - _position; // populated part of last chunk
- }
- var index = Array.IndexOf<byte>(_chunk, 0, _chunkOffset, partialCount);
- if (index != -1)
- {
- var stringLength = index - _chunkOffset;
- Position += stringLength + 1;
- return;
- }
-
- // the null terminator is not on the same chunk so keep looking starting with the next chunk
- var localChunkIndex = _chunkIndex + 1;
- var localPosition = localChunkIndex * __chunkSize;
- while (localPosition < _length)
+ ThrowIfDisposed();
+ var nullPosition = _byteBuffer.FindNullByte();
+ if (nullPosition == -1)
{
- var localChunk = _chunks[localChunkIndex];
- if (localChunkIndex < _chunks.Count - 1)
- {
- partialCount = __chunkSize; // all of any chunk but the last
- }
- else
- {
- partialCount = _length - localPosition; // populated part of last chunk
- }
- index = Array.IndexOf<byte>(localChunk, 0, 0, partialCount);
- if (index != -1)
- {
- localPosition += index;
- var stringLength = localPosition - _position;
- Position += stringLength + 1;
- return;
- }
- localChunkIndex++;
- localPosition += __chunkSize;
+ throw new FileFormatException("String is missing null terminator");
}
-
- throw new FileFormatException("String is missing null terminator.");
+ _byteBuffer.Position = nullPosition + 1;
}
/// <summary>
@@ -730,10 +509,12 @@ public void SkipCString()
/// <returns>A byte array.</returns>
public byte[] ToByteArray()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- var bytes = new byte[_position];
- CopyTo(0, bytes, 0, _position);
- return bytes;
+ ThrowIfDisposed();
+ var savedPosition = _byteBuffer.Position;
+ _byteBuffer.Position = 0;
+ var byteArray = _byteBuffer.ReadBytes(_byteBuffer.Length);
+ _byteBuffer.Position = savedPosition;
+ return byteArray;
}
/// <summary>
@@ -742,10 +523,8 @@ public byte[] ToByteArray()
/// <param name="value">The Boolean value.</param>
public void WriteBoolean(bool value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(1);
- _chunk[_chunkOffset] = value ? (byte)1 : (byte)0;
- Position++;
+ ThrowIfDisposed();
+ _byteBuffer.WriteByte(value ? (byte)1 : (byte)0);
}
/// <summary>
@@ -754,10 +533,8 @@ public void WriteBoolean(bool value)
/// <param name="value">A byte.</param>
public void WriteByte(byte value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(1);
- _chunk[_chunkOffset] = value;
- Position++;
+ ThrowIfDisposed();
+ _byteBuffer.WriteByte(value);
}
/// <summary>
@@ -766,20 +543,8 @@ public void WriteByte(byte value)
/// <param name="value">A byte array.</param>
public void WriteBytes(byte[] value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(value.Length);
- int sourceOffset = 0;
- int count = value.Length;
- while (count > 0)
- {
- // possibly straddles chunk boundary initially
- int available = __chunkSize - _chunkOffset;
- int partialCount = (count > available) ? available : count;
- Buffer.BlockCopy(value, sourceOffset, _chunk, _chunkOffset, partialCount);
- Position += partialCount;
- sourceOffset += partialCount;
- count -= partialCount;
- }
+ ThrowIfDisposed();
+ _byteBuffer.WriteBytes(value);
}
/// <summary>
@@ -788,21 +553,20 @@ public void WriteBytes(byte[] value)
/// <param name="value">A string.</param>
public void WriteCString(string value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- int maxLength = __utf8Encoding.GetMaxByteCount(value.Length) + 1;
- EnsureSpaceAvailable(maxLength);
- if (__chunkSize - _chunkOffset >= maxLength)
+ ThrowIfDisposed();
+
+ var maxLength = __utf8Encoding.GetMaxByteCount(value.Length) + 1;
+ var segment = _byteBuffer.WriteBackingBytes(maxLength);
+ if (segment.Count >= maxLength)
{
- int length = __utf8Encoding.GetBytes(value, 0, value.Length, _chunk, _chunkOffset);
- _chunk[_chunkOffset + length] = 0;
- Position += length + 1;
+ var length = __utf8Encoding.GetBytes(value, 0, value.Length, segment.Array, segment.Offset);
+ segment.Array[segment.Offset + length] = 0;
+ _byteBuffer.Position += length + 1;
}
else
{
- // straddles chunk boundary
- byte[] bytes = __utf8Encoding.GetBytes(value);
- WriteBytes(bytes);
- WriteByte(0);
+ _byteBuffer.WriteBytes(__utf8Encoding.GetBytes(value));
+ _byteBuffer.WriteByte(0);
}
}
@@ -812,17 +576,8 @@ public void WriteCString(string value)
/// <param name="value">The Double value.</param>
public void WriteDouble(double value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(8);
- if (__chunkSize - _chunkOffset >= 8)
- {
- Buffer.BlockCopy(BitConverter.GetBytes(value), 0, _chunk, _chunkOffset, 8);
- Position += 8;
- }
- else
- {
- WriteBytes(BitConverter.GetBytes(value)); // straddles chunk boundary
- }
+ ThrowIfDisposed();
+ _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
}
/// <summary>
@@ -831,20 +586,20 @@ public void WriteDouble(double value)
/// <param name="value">The Int32 value.</param>
public void WriteInt32(int value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(4);
- if (__chunkSize - _chunkOffset >= 4)
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.WriteBackingBytes(4);
+ if (segment.Count >= 4)
{
- // for int only we come out ahead with this code vs using BitConverter
- _chunk[_chunkOffset + 0] = (byte)(value);
- _chunk[_chunkOffset + 1] = (byte)(value >> 8);
- _chunk[_chunkOffset + 2] = (byte)(value >> 16);
- _chunk[_chunkOffset + 3] = (byte)(value >> 24);
- Position += 4;
+ segment.Array[segment.Offset + 0] = (byte)(value);
+ segment.Array[segment.Offset + 1] = (byte)(value >> 8);
+ segment.Array[segment.Offset + 2] = (byte)(value >> 16);
+ segment.Array[segment.Offset + 3] = (byte)(value >> 24);
+ _byteBuffer.Position += 4;
}
else
{
- WriteBytes(BitConverter.GetBytes(value)); // straddles chunk boundary
+ _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
}
}
@@ -854,17 +609,8 @@ public void WriteInt32(int value)
/// <param name="value">The Int64 value.</param>
public void WriteInt64(long value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(8);
- if (__chunkSize - _chunkOffset >= 8)
- {
- Buffer.BlockCopy(BitConverter.GetBytes(value), 0, _chunk, _chunkOffset, 8);
- Position += 8;
- }
- else
- {
- WriteBytes(BitConverter.GetBytes(value)); // straddles chunk boundary
- }
+ ThrowIfDisposed();
+ _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
}
/// <summary>
@@ -887,32 +633,32 @@ public void WriteObjectId(int timestamp, int machine, short pid, int increment)
/// <param name="objectId">The ObjectId.</param>
public void WriteObjectId(ObjectId objectId)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- var timestamp = objectId.Timestamp;
- var machine = objectId.Machine;
- var pid = objectId.Pid;
- var increment = objectId.Increment;
-
- EnsureSpaceAvailable(12);
- if (__chunkSize - _chunkOffset >= 12)
- {
- _chunk[_chunkOffset + 0] = (byte)(timestamp >> 24);
- _chunk[_chunkOffset + 1] = (byte)(timestamp >> 16);
- _chunk[_chunkOffset + 2] = (byte)(timestamp >> 8);
- _chunk[_chunkOffset + 3] = (byte)(timestamp);
- _chunk[_chunkOffset + 4] = (byte)(machine >> 16);
- _chunk[_chunkOffset + 5] = (byte)(machine >> 8);
- _chunk[_chunkOffset + 6] = (byte)(machine);
- _chunk[_chunkOffset + 7] = (byte)(pid >> 8);
- _chunk[_chunkOffset + 8] = (byte)(pid);
- _chunk[_chunkOffset + 9] = (byte)(increment >> 16);
- _chunk[_chunkOffset + 10] = (byte)(increment >> 8);
- _chunk[_chunkOffset + 11] = (byte)(increment);
- Position += 12;
+ ThrowIfDisposed();
+
+ var segment = _byteBuffer.WriteBackingBytes(12);
+ if (segment.Count >= 12)
+ {
+ var timestamp = objectId.Timestamp;
+ var machine = objectId.Machine;
+ var pid = objectId.Pid;
+ var increment = objectId.Increment;
+ segment.Array[segment.Offset + 0] = (byte)(timestamp >> 24);
+ segment.Array[segment.Offset + 1] = (byte)(timestamp >> 16);
+ segment.Array[segment.Offset + 2] = (byte)(timestamp >> 8);
+ segment.Array[segment.Offset + 3] = (byte)(timestamp);
+ segment.Array[segment.Offset + 4] = (byte)(machine >> 16);
+ segment.Array[segment.Offset + 5] = (byte)(machine >> 8);
+ segment.Array[segment.Offset + 6] = (byte)(machine);
+ segment.Array[segment.Offset + 7] = (byte)(pid >> 8);
+ segment.Array[segment.Offset + 8] = (byte)(pid);
+ segment.Array[segment.Offset + 9] = (byte)(increment >> 16);
+ segment.Array[segment.Offset + 10] = (byte)(increment >> 8);
+ segment.Array[segment.Offset + 11] = (byte)(increment);
+ _byteBuffer.Position += 12;
}
else
{
- WriteBytes(ObjectId.Pack(timestamp, machine, pid, increment)); // straddles chunk boundary
+ _byteBuffer.WriteBytes(objectId.ToByteArray());
}
}
@@ -922,27 +668,27 @@ public void WriteObjectId(ObjectId objectId)
/// <param name="value">The String value.</param>
public void WriteString(string value)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- int maxLength = __utf8Encoding.GetMaxByteCount(value.Length) + 5;
- EnsureSpaceAvailable(maxLength);
- if (__chunkSize - _chunkOffset >= maxLength)
+ ThrowIfDisposed();
+
+ var maxLength = __utf8Encoding.GetMaxByteCount(value.Length) + 5;
+ var segment = _byteBuffer.WriteBackingBytes(maxLength);
+ if (segment.Count >= maxLength)
{
- int length = __utf8Encoding.GetBytes(value, 0, value.Length, _chunk, _chunkOffset + 4); // write string first
- int lengthPlusOne = length + 1;
- _chunk[_chunkOffset + 0] = (byte)(lengthPlusOne); // now we know the length
- _chunk[_chunkOffset + 1] = (byte)(lengthPlusOne >> 8);
- _chunk[_chunkOffset + 2] = (byte)(lengthPlusOne >> 16);
- _chunk[_chunkOffset + 3] = (byte)(lengthPlusOne >> 24);
- _chunk[_chunkOffset + 4 + length] = 0;
- Position += length + 5;
+ var length = __utf8Encoding.GetBytes(value, 0, value.Length, segment.Array, segment.Offset + 4);
+ var lengthPlusOne = length + 1;
+ segment.Array[segment.Offset + 0] = (byte)(lengthPlusOne); // now we know the length
+ segment.Array[segment.Offset + 1] = (byte)(lengthPlusOne >> 8);
+ segment.Array[segment.Offset + 2] = (byte)(lengthPlusOne >> 16);
+ segment.Array[segment.Offset + 3] = (byte)(lengthPlusOne >> 24);
+ segment.Array[segment.Offset + 4 + length] = 0;
+ _byteBuffer.Position += length + 5;
}
else
{
- // straddles chunk boundary
- byte[] bytes = __utf8Encoding.GetBytes(value);
+ var bytes = __utf8Encoding.GetBytes(value);
WriteInt32(bytes.Length + 1);
- WriteBytes(bytes);
- WriteByte(0);
+ _byteBuffer.WriteBytes(bytes);
+ _byteBuffer.WriteByte(0);
}
}
@@ -952,43 +698,18 @@ public void WriteString(string value)
/// <param name="stream">The Stream.</param>
public void WriteTo(Stream stream)
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- if (_position > 0)
- {
- var wholeChunks = _position / __chunkSize;
- for (int chunkIndex = 0; chunkIndex < wholeChunks; chunkIndex++)
- {
- stream.Write(_chunks[chunkIndex], 0, __chunkSize);
- }
-
- var partialChunkSize = _position % __chunkSize;
- if (partialChunkSize != 0)
- {
- stream.Write(_chunks[wholeChunks], 0, partialChunkSize);
- }
- }
+ ThrowIfDisposed();
+ _byteBuffer.WriteTo(stream);
}
/// <summary>
/// Writes a 32-bit zero the the buffer.
/// </summary>
+ [Obsolete("Use WriteByte or WriteInt32 instead.")]
public void WriteZero()
{
- if (_disposed) { throw new ObjectDisposedException("BsonBuffer"); }
- EnsureSpaceAvailable(4);
- if (__chunkSize - _chunkOffset >= 4)
- {
- // for int only we come out ahead with this code vs using BitConverter
- _chunk[_chunkOffset + 0] = 0;
- _chunk[_chunkOffset + 1] = 0;
- _chunk[_chunkOffset + 2] = 0;
- _chunk[_chunkOffset + 3] = 0;
- Position += 4;
- }
- else
- {
- WriteBytes(BitConverter.GetBytes((int)0)); // straddles chunk boundary
- }
+ ThrowIfDisposed();
+ WriteInt32(0);
}
// private static methods
@@ -1002,7 +723,7 @@ private static string DecodeUtf8String(byte[] buffer, int index, int count)
// special case single character strings
case 1:
- var byte1 = buffer[index];
+ var byte1 = (int)buffer[index];
if (byte1 < __asciiStringTable.Length)
{
return __asciiStringTable[byte1];
@@ -1013,62 +734,61 @@ private static string DecodeUtf8String(byte[] buffer, int index, int count)
return __utf8Encoding.GetString(buffer, index, count);
}
- private static int IndexOfNull<TValue>(
- byte[] buffer,
- int index,
- int count,
- ref BsonTrieNode<TValue> bsonTrieNode)
+ // protected methods
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
{
- for (; count > 0; index++, count--)
+ if (!_disposed)
{
- // bsonTrieNode might be null on entry or it might become null while navigating the trie
- if (bsonTrieNode == null)
+ if (disposing)
{
- return Array.IndexOf<byte>(buffer, 0, index, count);
- }
-
- var keyByte = buffer[index];
- if (keyByte == 0)
- {
- return index;
+ if (_byteBuffer != null)
+ {
+ if (_disposeByteBuffer)
+ {
+ _byteBuffer.Dispose();
+ }
+ _byteBuffer = null;
+ }
}
-
- bsonTrieNode = bsonTrieNode.GetChild(keyByte); // might return null
+ _disposed = true;
}
-
- return -1;
}
- // private methods
- private void EnsureDataAvailable(int needed)
+ /// <summary>
+ /// Throws if disposed.
+ /// </summary>
+ /// <exception cref="System.ObjectDisposedException"></exception>
+ protected void ThrowIfDisposed()
{
- if (_length - _position < needed)
+ if (_disposed)
{
- var available = _length - _position;
- var message = string.Format(
- "Not enough input bytes available. Needed {0}, but only {1} are available (at position {2}).",
- needed, available, _position);
- throw new EndOfStreamException(message);
+ throw new ObjectDisposedException(GetType().Name);
}
}
-
- private void EnsureSpaceAvailable(int needed)
+
+ // private methods
+ private string ReadCString(int nullPosition)
{
- if (_capacity - _position < needed)
+ if (nullPosition == -1)
{
- // either we have no chunks or we just crossed a chunk boundary landing at chunkOffset 0
- if (_chunk == null)
- {
- _chunk = GetChunk();
- _chunks.Add(_chunk);
- _capacity += __chunkSize;
- }
+ throw new BsonSerializationException("Missing null terminator.");
+ }
- while (_capacity - _position < needed)
- {
- _chunks.Add(GetChunk());
- _capacity += __chunkSize;
- }
+ var length = nullPosition - _byteBuffer.Position + 1;
+ var segment = _byteBuffer.WriteBackingBytes(length);
+ if (segment.Count >= length)
+ {
+ _byteBuffer.Position += length;
+ return DecodeUtf8String(segment.Array, segment.Offset, length - 1);
+ }
+ else
+ {
+ var bytes = _byteBuffer.ReadBytes(length);
+ return DecodeUtf8String(bytes, 0, length - 1);
}
}
}
View
105 MongoDB.Bson/IO/BsonChunk.cs
@@ -0,0 +1,105 @@
+/* Copyright 2010-2013 10gen 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
+{
+ /// <summary>
+ /// Represents a BSON buffer chunk.
+ /// </summary>
+ internal class BsonChunk
+ {
+ // private fields
+ private readonly byte[] _bytes;
+ private readonly BsonChunkPool _chunkPool;
+ private int _referenceCount;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BsonChunk"/> class.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="chunkPool">The chunk pool.</param>
+ /// <exception cref="System.ArgumentNullException">
+ /// bytes
+ /// or
+ /// pool
+ /// </exception>
+ public BsonChunk(byte[] bytes, BsonChunkPool chunkPool)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+ if (chunkPool == null)
+ {
+ throw new ArgumentNullException("chunkPool");
+ }
+
+ _bytes = bytes;
+ _chunkPool = chunkPool;
+ }
+
+ // public properties
+ /// <summary>
+ /// Gets the bytes.
+ /// </summary>
+ /// <value>
+ /// The bytes.
+ /// </value>
+ public byte[] Bytes
+ {
+ get { return _bytes; }
+ }
+
+ /// <summary>
+ /// Gets the reference count.
+ /// </summary>
+ /// <value>
+ /// The reference count.
+ /// </value>
+ public int ReferenceCount
+ {
+ get { return _referenceCount; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Decrements the reference count.
+ /// </summary>
+ /// <exception cref="BsonInternalException">Reference count is less than or equal to zero.</exception>
+ public void DecrementReferenceCount()
+ {
+ if (_referenceCount <= 0)
+ {
+ throw new BsonInternalException("Reference count is less than or equal to zero.");
+ }
+
+ if (--_referenceCount == 0 && _chunkPool != null)
+ {
+ _chunkPool.ReleaseChunk(this);
+ }
+ }
+
+ /// <summary>
+ /// Increments the reference count.
+ /// </summary>
+ public void IncrementReferenceCount()
+ {
+ _referenceCount++;
+ }
+ }
+}
View
128 MongoDB.Bson/IO/BsonChunkPool.cs
@@ -0,0 +1,128 @@
+/* Copyright 2010-2013 10gen 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;
+using System.Collections.Generic;
+
+namespace MongoDB.Bson.IO
+{
+ /// <summary>
+ /// Represents a pool of chunks used by BsonBuffer.
+ /// </summary>
+ public class BsonChunkPool
+ {
+ // private static fields
+ private static BsonChunkPool __default = new BsonChunkPool(2048, 16 * 1024); // 32MiB of 16KiB chunks
+
+ // private fields
+ private readonly object _lock = new object();
+ private readonly Stack<BsonChunk> _chunks = new Stack<BsonChunk>();
+ private readonly int _maxPoolSize;
+ private readonly int _chunkSize;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BsonChunkPool"/> class.
+ /// </summary>
+ /// <param name="maxPoolSize">The maximum number of chunks to keep in the pool.</param>
+ /// <param name="chunkSize">The size of each chunk.</param>
+ public BsonChunkPool(int maxPoolSize, int chunkSize)
+ {
+ _maxPoolSize = maxPoolSize;
+ _chunkSize = chunkSize;
+ }
+
+ // public static properties
+ /// <summary>
+ /// Gets the default chunk pool.
+ /// </summary>
+ /// <value>
+ /// The default chunk pool.
+ /// </value>
+ public static BsonChunkPool Default
+ {
+ get { return __default; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("Default");
+ }
+ __default = value;
+ }
+ }
+
+ // public properties
+ /// <summary>
+ /// Gets the chunk size.
+ /// </summary>
+ /// <value>
+ /// The chunk size.
+ /// </value>
+ public int ChunkSize
+ {
+ get { return _chunkSize; }
+ }
+
+ /// <summary>
+ /// Gets or sets the max pool size.
+ /// </summary>
+ public int MaxPoolSize
+ {
+ get { return _maxPoolSize; }
+ }
+
+ // internal methods
+ /// <summary>
+ /// Acquires a chunk.
+ /// </summary>
+ /// <returns></returns>
+ internal BsonChunk AcquireChunk()
+ {
+ lock (_lock)
+ {
+ if (_chunks.Count > 0)
+ {
+ return _chunks.Pop();
+ }
+ }
+
+ // release the lock before allocating memory
+ var bytes = new byte[_chunkSize];
+ return new BsonChunk(bytes, this);
+ }
+
+ /// <summary>
+ /// Releases a chunk.
+ /// </summary>
+ /// <param name="chunk">The chunk.</param>
+ internal void ReleaseChunk(BsonChunk chunk)
+ {
+ if (chunk.ReferenceCount != 0)
+ {
+ new BsonInternalException("A chunk is being returned to the pool and the reference count is not zero.");
+ }
+
+ lock (_lock)
+ {
+ if (_chunks.Count < _maxPoolSize)
+ {
+ _chunks.Push(chunk);
+ }
+ // otherwise just let it get garbage collected
+ }
+ }
+ }
+}
View
1 MongoDB.Bson/IO/BsonDocumentWriter.cs
@@ -491,6 +491,7 @@ protected override void Dispose(bool disposing)
{
Close();
}
+ base.Dispose(disposing);
}
// private methods
View
56 MongoDB.Bson/IO/BsonReader.cs
@@ -16,6 +16,7 @@
using System;
using System.IO;
using System.Linq;
+using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Bson.IO
{
@@ -106,7 +107,7 @@ public static BsonReader Create(BsonBuffer buffer)
/// <returns>A BsonReader.</returns>
public static BsonReader Create(BsonBuffer buffer, BsonBinaryReaderSettings settings)
{
- return new BsonBinaryReader(buffer, settings);
+ return new BsonBinaryReader(buffer, false, settings);
}
/// <summary>
@@ -169,9 +170,8 @@ public static BsonReader Create(Stream stream)
/// <returns>A BsonReader.</returns>
public static BsonReader Create(Stream stream, BsonBinaryReaderSettings settings)
{
- var reader = new BsonBinaryReader(null, settings);
- reader.Buffer.LoadFrom(stream);
- return reader;
+ var byteBuffer = ByteBufferFactory.LoadFrom(stream);
+ return new BsonBinaryReader(new BsonBuffer(byteBuffer, true), true, settings);
}
/// <summary>
@@ -663,6 +663,54 @@ public void ReadObjectId(string name, out int timestamp, out int machine, out sh
}
/// <summary>
+ /// Reads a raw BSON array.
+ /// </summary>
+ /// <returns>The raw BSON array.</returns>
+ public virtual IByteBuffer ReadRawBsonArray()
+ {
+ // overridden in BsonBinaryReader
+ var array = BsonDocumentSerializer.Instance.Deserialize(this, typeof(BsonArray), null);
+ var bytes = array.ToBson();
+ return new ByteArrayBuffer(bytes, 0, bytes.Length, true);
+ }
+
+ /// <summary>
+ /// Reads a raw BSON array.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <returns>
+ /// The raw BSON array.
+ /// </returns>
+ public IByteBuffer ReadRawBsonArray(string name)
+ {
+ VerifyName(name);
+ return ReadRawBsonArray();
+ }
+
+ /// <summary>
+ /// Reads a raw BSON document.
+ /// </summary>
+ /// <returns>The raw BSON document.</returns>
+ public virtual IByteBuffer ReadRawBsonDocument()
+ {
+ // overridden in BsonBinaryReader
+ var document = BsonDocumentSerializer.Instance.Deserialize(this, typeof(BsonDocument), null);
+ var bytes = document.ToBson();
+ return new ByteArrayBuffer(bytes, 0, bytes.Length, true);
+ }
+
+ /// <summary>
+ /// Reads a raw BSON document.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <returns>The raw BSON document.</returns>
+ public IByteBuffer ReadRawBsonDocument(string name)
+ {
+ VerifyName(name);
+ return ReadRawBsonDocument();
+ }
+
+ /// <summary>
/// Reads a BSON regular expression from the reader.
/// </summary>
/// <returns>A BsonRegularExpression.</returns>
View
58 MongoDB.Bson/IO/BsonWriter.cs
@@ -16,6 +16,8 @@
using System;
using System.IO;
using System.Linq;
+using MongoDB.Bson.Serialization;
+using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Bson.IO
{
@@ -177,7 +179,7 @@ public static BsonWriter Create(Stream stream)
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(Stream stream, BsonBinaryWriterSettings settings)
{
- return new BsonBinaryWriter(stream, null, BsonBinaryWriterSettings.Defaults);
+ return new BsonBinaryWriter(stream, null, settings);
}
/// <summary>
@@ -561,6 +563,56 @@ public void WriteObjectId(string name, int timestamp, int machine, short pid, in
}
/// <summary>
+ /// Writes a raw BSON array.
+ /// </summary>
+ /// <param name="slice">The byte buffer containing the raw BSON array.</param>
+ public virtual void WriteRawBsonArray(IByteBuffer slice)
+ {
+ // overridden in BsonBinaryWriter
+ using (var bsonReader = new BsonBinaryReader(new BsonBuffer(slice, false), true, BsonBinaryReaderSettings.Defaults))
+ {
+ var array = BsonSerializer.Deserialize<BsonArray>(bsonReader);
+ BsonArraySerializer.Instance.Serialize(this, typeof(BsonArray), array, null);
+ }
+ }
+
+ /// <summary>
+ /// Writes a raw BSON array.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="slice">The byte buffer containing the raw BSON array.</param>
+ public void WriteRawBsonArray(string name, IByteBuffer slice)
+ {
+ WriteName(name);
+ WriteRawBsonArray(slice);
+ }
+
+ /// <summary>
+ /// Writes a raw BSON document.
+ /// </summary>
+ /// <param name="slice">The byte buffer containing the raw BSON document.</param>
+ public virtual void WriteRawBsonDocument(IByteBuffer slice)
+ {
+ // overridden in BsonBinaryWriter
+ using (var bsonReader = new BsonBinaryReader(new BsonBuffer(slice, false), true, BsonBinaryReaderSettings.Defaults))
+ {
+ var document = BsonSerializer.Deserialize<BsonDocument>(bsonReader);
+ BsonDocumentSerializer.Instance.Serialize(this, typeof(BsonDocument), document, null);
+ }
+ }
+
+ /// <summary>
+ /// Writes a raw BSON document.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="slice">The byte buffer containing the raw BSON document.</param>
+ public void WriteRawBsonDocument(string name, IByteBuffer slice)
+ {
+ WriteName(name);
+ WriteRawBsonDocument(slice);
+ }
+
+ /// <summary>
/// Writes a BSON regular expression to the writer.
/// </summary>
/// <param name="regex">A BsonRegularExpression.</param>
@@ -756,7 +808,9 @@ protected void CheckElementName(string name)
/// Disposes of any resources used by the writer.
/// </summary>
/// <param name="disposing">True if called from Dispose.</param>
- protected abstract void Dispose(bool disposing);
+ protected virtual void Dispose(bool disposing)
+ {
+ }
/// <summary>
/// Throws an InvalidOperationException when the method called is not valid for the current ContextType.
View
533 MongoDB.Bson/IO/ByteArrayBuffer.cs
@@ -0,0 +1,533 @@
+/* Copyright 2010-2013 10gen 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;
+using System.IO;
+
+namespace MongoDB.Bson.IO
+{
+ /// <summary>
+ /// A BSON buffer that is backed by a byte array.
+ /// </summary>
+ public class ByteArrayBuffer : IByteBuffer
+ {
+ // private fields
+ private bool _disposed;
+ private byte[] _bytes;
+ private int _sliceOffset;
+ private int _capacity;
+ private int _length;
+ private int _position;
+ private bool _isReadOnly;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ByteArrayBuffer"/> class.
+ /// </summary>
+ /// <param name="bytes">The backing bytes.</param>
+ /// <param name="sliceOffset">The offset where the slice begins.</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <param name="isReadOnly">Whether the buffer is read only.</param>
+ /// <exception cref="System.ArgumentNullException">bytes</exception>
+ public ByteArrayBuffer(byte[] bytes, int sliceOffset, int length, bool isReadOnly)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+
+ _bytes = bytes;
+ _sliceOffset = sliceOffset;
+ _capacity = isReadOnly ? length : bytes.Length - _sliceOffset;
+ _length = length;
+ _isReadOnly = isReadOnly;
+ _position = 0;
+ }
+
+ // public properties
+ /// <summary>
+ /// Gets or sets the capacity.
+ /// </summary>
+ /// <value>
+ /// The capacity.
+ /// </value>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.NotSupportedException">The capacity of a ByteArrayBuffer cannot be changed.</exception>
+ public int Capacity
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return _capacity;
+ }
+ set
+ {
+ ThrowIfDisposed();
+ throw new NotSupportedException("The capacity of a ByteArrayBuffer cannot be changed.");
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is read only.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
+ /// </value>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ public bool IsReadOnly
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return _isReadOnly;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the length.
+ /// </summary>
+ /// <value>
+ /// The length.
+ /// </value>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.InvalidOperationException">The length of a read only buffer cannot be changed.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">Length</exception>
+ public int Length
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return _length;
+ }
+ set
+ {
+ ThrowIfDisposed();
+ EnsureIsWritable();
+ if (value < 0 || value > _capacity)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+ _length = value;
+ if (_position > _length)
+ {
+ _position = _length;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ /// <value>
+ /// The position.
+ /// </value>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">Position</exception>
+ public int Position
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return _position;
+ }
+ set
+ {
+ ThrowIfDisposed();
+ if (value < 0 || value > _capacity)
+ {
+ throw new ArgumentOutOfRangeException("Position");
+ }
+ _position = value;
+ if (_length < _position)
+ {
+ _length = _position;
+ }
+ }
+ }
+
+ // protected properties
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="ByteArrayBuffer"/> is disposed.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if disposed; otherwise, <c>false</c>.
+ /// </value>
+ protected bool Disposed
+ {
+ get { return _disposed; }
+ }
+
+ /// <summary>
+ /// Gets the slice offset.
+ /// </summary>
+ /// <value>
+ /// The slice offset.
+ /// </value>
+ protected int SliceOffset
+ {
+ get { return _sliceOffset; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Clears this instance.
+ /// </summary>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
+ public virtual void Clear()
+ {
+ ThrowIfDisposed();
+ EnsureIsWritable();
+ _position = 0;
+ _length = 0;
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Finds the next null byte.
+ /// </summary>
+ /// <returns>
+ /// The position of the next null byte.
+ /// </returns>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ public int FindNullByte()
+ {
+ ThrowIfDisposed();
+ return Array.IndexOf<byte>(_bytes, 0, _position, _length - _position);
+ }
+
+ /// <summary>
+ /// Gets a slice of this buffer.
+ /// </summary>
+ /// <param name="position">The position of the start of the slice.</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <returns>
+ /// A slice of this buffer.
+ /// </returns>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.InvalidOperationException">GetSlice can only be called for read only buffers.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// position
+ /// or
+ /// length
+ /// </exception>
+ public virtual IByteBuffer GetSlice(int position, int length)
+ {
+ ThrowIfDisposed();
+ EnsureIsReadOnly();
+ if (position < 0 || position >= _length)
+ {
+ throw new ArgumentOutOfRangeException("position");
+ }
+ if (length <= 0 || length > _length - position)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ return new ByteArrayBuffer(_bytes, _sliceOffset + position, length, true);
+ }
+
+ /// <summary>
+ /// Loads the buffer from a stream.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ /// <param name="count">The count.</param>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
+ /// <exception cref="System.ArgumentNullException">stream</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">count</exception>
+ public void LoadFrom(Stream stream, int count)
+ {
+ ThrowIfDisposed();
+ EnsureIsWritable();
+ if (stream == null)
+ {
+ throw new ArgumentNullException("stream");
+ }
+ if (count > _capacity - _position)
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+
+ EnsureSpaceAvailable(count);
+ var position = _position; // don't advance position
+ while (count > 0)
+ {
+ var bytesRead = stream.Read(_bytes, _sliceOffset + position, count);
+ position += bytesRead;
+ count -= bytesRead;
+ }
+ if (_length < position)
+ {
+ _length = position;
+ }
+ }
+
+ /// <summary>
+ /// Makes this buffer read only.
+ /// </summary>
+ /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
+ public void MakeReadOnly()
+ {
+ ThrowIfDisposed();
+ _isReadOnly = true;
+ }
+
+ /// <summary>
+ /// Read directly from the backing bytes. The returned ArraySegment points directly to the backing bytes for
+ /// the current position and you can read the bytes directly from there. If the backing bytes happen to span