Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
361c933
commit 90d4969
Showing
2 changed files
with
295 additions
and
23 deletions.
There are no files selected for viewing
213 changes: 213 additions & 0 deletions
213
csharp/src/Google.Protobuf.Test/Buffers/ArrayBufferWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
#region Copyright notice and license | ||
// Protocol Buffers - Google's data interchange format | ||
// Copyright 2008 Google Inc. All rights reserved. | ||
// https://developers.google.com/protocol-buffers/ | ||
// | ||
// Redistribution and use in source and binary forms, with or without | ||
// modification, are permitted provided that the following conditions are | ||
// met: | ||
// | ||
// * Redistributions of source code must retain the above copyright | ||
// notice, this list of conditions and the following disclaimer. | ||
// * Redistributions in binary form must reproduce the above | ||
// copyright notice, this list of conditions and the following disclaimer | ||
// in the documentation and/or other materials provided with the | ||
// distribution. | ||
// * Neither the name of Google Inc. nor the names of its | ||
// contributors may be used to endorse or promote products derived from | ||
// this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
#endregion | ||
|
||
using System; | ||
using System.Buffers; | ||
using System.Diagnostics; | ||
|
||
namespace Google.Protobuf.Buffers | ||
{ | ||
/// <summary> | ||
/// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written. | ||
/// | ||
/// ArrayBufferWriter is originally from corefx, and has been contributed to Protobuf | ||
/// https://github.com/dotnet/runtime/blob/071da4c41aa808c949a773b92dca6f88de9d11f3/src/libraries/Common/src/System/Buffers/ArrayBufferWriter.cs | ||
/// </summary> | ||
internal sealed class ArrayBufferWriter<T> : IBufferWriter<T> | ||
{ | ||
private T[] _buffer; | ||
private int _index; | ||
|
||
private const int DefaultInitialBufferSize = 256; | ||
|
||
/// <summary> | ||
/// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to, | ||
/// with the default initial capacity. | ||
/// </summary> | ||
public ArrayBufferWriter() | ||
{ | ||
_buffer = new T[0]; | ||
_index = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to, | ||
/// with an initial capacity specified. | ||
/// </summary> | ||
/// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param> | ||
/// <exception cref="ArgumentException"> | ||
/// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0). | ||
/// </exception> | ||
public ArrayBufferWriter(int initialCapacity) | ||
{ | ||
if (initialCapacity <= 0) | ||
throw new ArgumentException(nameof(initialCapacity)); | ||
|
||
_buffer = new T[initialCapacity]; | ||
_index = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>. | ||
/// </summary> | ||
public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index); | ||
|
||
/// <summary> | ||
/// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>. | ||
/// </summary> | ||
public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index); | ||
|
||
/// <summary> | ||
/// Returns the amount of data written to the underlying buffer so far. | ||
/// </summary> | ||
public int WrittenCount => _index; | ||
|
||
/// <summary> | ||
/// Returns the total amount of space within the underlying buffer. | ||
/// </summary> | ||
public int Capacity => _buffer.Length; | ||
|
||
/// <summary> | ||
/// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow. | ||
/// </summary> | ||
public int FreeCapacity => _buffer.Length - _index; | ||
|
||
/// <summary> | ||
/// Clears the data written to the underlying buffer. | ||
/// </summary> | ||
/// <remarks> | ||
/// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it. | ||
/// </remarks> | ||
public void Clear() | ||
{ | ||
Debug.Assert(_buffer.Length >= _index); | ||
_buffer.AsSpan(0, _index).Clear(); | ||
_index = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Notifies <see cref="IBufferWriter{T}"/> that <paramref name="count"/> amount of data was written to the output <see cref="Span{T}"/>/<see cref="Memory{T}"/> | ||
/// </summary> | ||
/// <exception cref="ArgumentException"> | ||
/// Thrown when <paramref name="count"/> is negative. | ||
/// </exception> | ||
/// <exception cref="InvalidOperationException"> | ||
/// Thrown when attempting to advance past the end of the underlying buffer. | ||
/// </exception> | ||
/// <remarks> | ||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. | ||
/// </remarks> | ||
public void Advance(int count) | ||
{ | ||
if (count < 0) | ||
throw new ArgumentException(nameof(count)); | ||
|
||
if (_index > _buffer.Length - count) | ||
throw new InvalidOperationException("Advanced past capacity."); | ||
|
||
_index += count; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>). | ||
/// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned. | ||
/// </summary> | ||
/// <exception cref="ArgumentException"> | ||
/// Thrown when <paramref name="sizeHint"/> is negative. | ||
/// </exception> | ||
/// <remarks> | ||
/// This will never return an empty <see cref="Memory{T}"/>. | ||
/// </remarks> | ||
/// <remarks> | ||
/// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. | ||
/// </remarks> | ||
/// <remarks> | ||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. | ||
/// </remarks> | ||
public Memory<T> GetMemory(int sizeHint = 0) | ||
{ | ||
CheckAndResizeBuffer(sizeHint); | ||
Debug.Assert(_buffer.Length > _index); | ||
return _buffer.AsMemory(_index); | ||
} | ||
|
||
/// <summary> | ||
/// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>). | ||
/// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned. | ||
/// </summary> | ||
/// <exception cref="ArgumentException"> | ||
/// Thrown when <paramref name="sizeHint"/> is negative. | ||
/// </exception> | ||
/// <remarks> | ||
/// This will never return an empty <see cref="Span{T}"/>. | ||
/// </remarks> | ||
/// <remarks> | ||
/// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. | ||
/// </remarks> | ||
/// <remarks> | ||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. | ||
/// </remarks> | ||
public Span<T> GetSpan(int sizeHint = 0) | ||
{ | ||
CheckAndResizeBuffer(sizeHint); | ||
Debug.Assert(_buffer.Length > _index); | ||
return _buffer.AsSpan(_index); | ||
} | ||
|
||
private void CheckAndResizeBuffer(int sizeHint) | ||
{ | ||
if (sizeHint < 0) | ||
throw new ArgumentException(nameof(sizeHint)); | ||
|
||
if (sizeHint == 0) | ||
{ | ||
sizeHint = 1; | ||
} | ||
|
||
if (sizeHint > FreeCapacity) | ||
{ | ||
int growBy = Math.Max(sizeHint, _buffer.Length); | ||
|
||
if (_buffer.Length == 0) | ||
{ | ||
growBy = Math.Max(growBy, DefaultInitialBufferSize); | ||
} | ||
|
||
int newSize = checked(_buffer.Length + growBy); | ||
|
||
Array.Resize(ref _buffer, newSize); | ||
} | ||
|
||
Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters