1ffe3c632Sopenharmony_ci#region Copyright notice and license 2ffe3c632Sopenharmony_ci// Protocol Buffers - Google's data interchange format 3ffe3c632Sopenharmony_ci// Copyright 2008 Google Inc. All rights reserved. 4ffe3c632Sopenharmony_ci// https://developers.google.com/protocol-buffers/ 5ffe3c632Sopenharmony_ci// 6ffe3c632Sopenharmony_ci// Redistribution and use in source and binary forms, with or without 7ffe3c632Sopenharmony_ci// modification, are permitted provided that the following conditions are 8ffe3c632Sopenharmony_ci// met: 9ffe3c632Sopenharmony_ci// 10ffe3c632Sopenharmony_ci// * Redistributions of source code must retain the above copyright 11ffe3c632Sopenharmony_ci// notice, this list of conditions and the following disclaimer. 12ffe3c632Sopenharmony_ci// * Redistributions in binary form must reproduce the above 13ffe3c632Sopenharmony_ci// copyright notice, this list of conditions and the following disclaimer 14ffe3c632Sopenharmony_ci// in the documentation and/or other materials provided with the 15ffe3c632Sopenharmony_ci// distribution. 16ffe3c632Sopenharmony_ci// * Neither the name of Google Inc. nor the names of its 17ffe3c632Sopenharmony_ci// contributors may be used to endorse or promote products derived from 18ffe3c632Sopenharmony_ci// this software without specific prior written permission. 19ffe3c632Sopenharmony_ci// 20ffe3c632Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21ffe3c632Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22ffe3c632Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23ffe3c632Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24ffe3c632Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25ffe3c632Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26ffe3c632Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27ffe3c632Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28ffe3c632Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29ffe3c632Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30ffe3c632Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31ffe3c632Sopenharmony_ci#endregion 32ffe3c632Sopenharmony_ci 33ffe3c632Sopenharmony_ciusing System; 34ffe3c632Sopenharmony_ciusing System.Buffers; 35ffe3c632Sopenharmony_ciusing System.Diagnostics; 36ffe3c632Sopenharmony_ci 37ffe3c632Sopenharmony_cinamespace Google.Protobuf.Buffers 38ffe3c632Sopenharmony_ci{ 39ffe3c632Sopenharmony_ci /// <summary> 40ffe3c632Sopenharmony_ci /// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written. 41ffe3c632Sopenharmony_ci /// 42ffe3c632Sopenharmony_ci /// ArrayBufferWriter is originally from corefx, and has been contributed to Protobuf 43ffe3c632Sopenharmony_ci /// https://github.com/dotnet/runtime/blob/071da4c41aa808c949a773b92dca6f88de9d11f3/src/libraries/Common/src/System/Buffers/ArrayBufferWriter.cs 44ffe3c632Sopenharmony_ci /// </summary> 45ffe3c632Sopenharmony_ci internal sealed class ArrayBufferWriter<T> : IBufferWriter<T> 46ffe3c632Sopenharmony_ci { 47ffe3c632Sopenharmony_ci private T[] _buffer; 48ffe3c632Sopenharmony_ci private int _index; 49ffe3c632Sopenharmony_ci 50ffe3c632Sopenharmony_ci private const int DefaultInitialBufferSize = 256; 51ffe3c632Sopenharmony_ci 52ffe3c632Sopenharmony_ci /// <summary> 53ffe3c632Sopenharmony_ci /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to, 54ffe3c632Sopenharmony_ci /// with the default initial capacity. 55ffe3c632Sopenharmony_ci /// </summary> 56ffe3c632Sopenharmony_ci public ArrayBufferWriter() 57ffe3c632Sopenharmony_ci { 58ffe3c632Sopenharmony_ci _buffer = new T[0]; 59ffe3c632Sopenharmony_ci _index = 0; 60ffe3c632Sopenharmony_ci } 61ffe3c632Sopenharmony_ci 62ffe3c632Sopenharmony_ci /// <summary> 63ffe3c632Sopenharmony_ci /// Userful for testing writing to buffer writer with a lot of small segments. 64ffe3c632Sopenharmony_ci /// If set, it limits the max number of bytes by which the buffer grows by at once. 65ffe3c632Sopenharmony_ci /// </summary> 66ffe3c632Sopenharmony_ci public int? MaxGrowBy { get; set; } 67ffe3c632Sopenharmony_ci 68ffe3c632Sopenharmony_ci /// <summary> 69ffe3c632Sopenharmony_ci /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to, 70ffe3c632Sopenharmony_ci /// with an initial capacity specified. 71ffe3c632Sopenharmony_ci /// </summary> 72ffe3c632Sopenharmony_ci /// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param> 73ffe3c632Sopenharmony_ci /// <exception cref="ArgumentException"> 74ffe3c632Sopenharmony_ci /// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0). 75ffe3c632Sopenharmony_ci /// </exception> 76ffe3c632Sopenharmony_ci public ArrayBufferWriter(int initialCapacity) 77ffe3c632Sopenharmony_ci { 78ffe3c632Sopenharmony_ci if (initialCapacity <= 0) 79ffe3c632Sopenharmony_ci throw new ArgumentException(nameof(initialCapacity)); 80ffe3c632Sopenharmony_ci 81ffe3c632Sopenharmony_ci _buffer = new T[initialCapacity]; 82ffe3c632Sopenharmony_ci _index = 0; 83ffe3c632Sopenharmony_ci } 84ffe3c632Sopenharmony_ci 85ffe3c632Sopenharmony_ci /// <summary> 86ffe3c632Sopenharmony_ci /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>. 87ffe3c632Sopenharmony_ci /// </summary> 88ffe3c632Sopenharmony_ci public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index); 89ffe3c632Sopenharmony_ci 90ffe3c632Sopenharmony_ci /// <summary> 91ffe3c632Sopenharmony_ci /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>. 92ffe3c632Sopenharmony_ci /// </summary> 93ffe3c632Sopenharmony_ci public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index); 94ffe3c632Sopenharmony_ci 95ffe3c632Sopenharmony_ci /// <summary> 96ffe3c632Sopenharmony_ci /// Returns the amount of data written to the underlying buffer so far. 97ffe3c632Sopenharmony_ci /// </summary> 98ffe3c632Sopenharmony_ci public int WrittenCount => _index; 99ffe3c632Sopenharmony_ci 100ffe3c632Sopenharmony_ci /// <summary> 101ffe3c632Sopenharmony_ci /// Returns the total amount of space within the underlying buffer. 102ffe3c632Sopenharmony_ci /// </summary> 103ffe3c632Sopenharmony_ci public int Capacity => _buffer.Length; 104ffe3c632Sopenharmony_ci 105ffe3c632Sopenharmony_ci /// <summary> 106ffe3c632Sopenharmony_ci /// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow. 107ffe3c632Sopenharmony_ci /// </summary> 108ffe3c632Sopenharmony_ci public int FreeCapacity => _buffer.Length - _index; 109ffe3c632Sopenharmony_ci 110ffe3c632Sopenharmony_ci /// <summary> 111ffe3c632Sopenharmony_ci /// Clears the data written to the underlying buffer. 112ffe3c632Sopenharmony_ci /// </summary> 113ffe3c632Sopenharmony_ci /// <remarks> 114ffe3c632Sopenharmony_ci /// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it. 115ffe3c632Sopenharmony_ci /// </remarks> 116ffe3c632Sopenharmony_ci public void Clear() 117ffe3c632Sopenharmony_ci { 118ffe3c632Sopenharmony_ci Debug.Assert(_buffer.Length >= _index); 119ffe3c632Sopenharmony_ci _buffer.AsSpan(0, _index).Clear(); 120ffe3c632Sopenharmony_ci _index = 0; 121ffe3c632Sopenharmony_ci } 122ffe3c632Sopenharmony_ci 123ffe3c632Sopenharmony_ci /// <summary> 124ffe3c632Sopenharmony_ci /// 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}"/> 125ffe3c632Sopenharmony_ci /// </summary> 126ffe3c632Sopenharmony_ci /// <exception cref="ArgumentException"> 127ffe3c632Sopenharmony_ci /// Thrown when <paramref name="count"/> is negative. 128ffe3c632Sopenharmony_ci /// </exception> 129ffe3c632Sopenharmony_ci /// <exception cref="InvalidOperationException"> 130ffe3c632Sopenharmony_ci /// Thrown when attempting to advance past the end of the underlying buffer. 131ffe3c632Sopenharmony_ci /// </exception> 132ffe3c632Sopenharmony_ci /// <remarks> 133ffe3c632Sopenharmony_ci /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. 134ffe3c632Sopenharmony_ci /// </remarks> 135ffe3c632Sopenharmony_ci public void Advance(int count) 136ffe3c632Sopenharmony_ci { 137ffe3c632Sopenharmony_ci if (count < 0) 138ffe3c632Sopenharmony_ci throw new ArgumentException(nameof(count)); 139ffe3c632Sopenharmony_ci 140ffe3c632Sopenharmony_ci if (_index > _buffer.Length - count) 141ffe3c632Sopenharmony_ci throw new InvalidOperationException("Advanced past capacity."); 142ffe3c632Sopenharmony_ci 143ffe3c632Sopenharmony_ci _index += count; 144ffe3c632Sopenharmony_ci } 145ffe3c632Sopenharmony_ci 146ffe3c632Sopenharmony_ci /// <summary> 147ffe3c632Sopenharmony_ci /// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>). 148ffe3c632Sopenharmony_ci /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned. 149ffe3c632Sopenharmony_ci /// </summary> 150ffe3c632Sopenharmony_ci /// <exception cref="ArgumentException"> 151ffe3c632Sopenharmony_ci /// Thrown when <paramref name="sizeHint"/> is negative. 152ffe3c632Sopenharmony_ci /// </exception> 153ffe3c632Sopenharmony_ci /// <remarks> 154ffe3c632Sopenharmony_ci /// This will never return an empty <see cref="Memory{T}"/>. 155ffe3c632Sopenharmony_ci /// </remarks> 156ffe3c632Sopenharmony_ci /// <remarks> 157ffe3c632Sopenharmony_ci /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. 158ffe3c632Sopenharmony_ci /// </remarks> 159ffe3c632Sopenharmony_ci /// <remarks> 160ffe3c632Sopenharmony_ci /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. 161ffe3c632Sopenharmony_ci /// </remarks> 162ffe3c632Sopenharmony_ci public Memory<T> GetMemory(int sizeHint = 0) 163ffe3c632Sopenharmony_ci { 164ffe3c632Sopenharmony_ci CheckAndResizeBuffer(sizeHint); 165ffe3c632Sopenharmony_ci Debug.Assert(_buffer.Length > _index); 166ffe3c632Sopenharmony_ci return _buffer.AsMemory(_index); 167ffe3c632Sopenharmony_ci } 168ffe3c632Sopenharmony_ci 169ffe3c632Sopenharmony_ci /// <summary> 170ffe3c632Sopenharmony_ci /// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>). 171ffe3c632Sopenharmony_ci /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned. 172ffe3c632Sopenharmony_ci /// </summary> 173ffe3c632Sopenharmony_ci /// <exception cref="ArgumentException"> 174ffe3c632Sopenharmony_ci /// Thrown when <paramref name="sizeHint"/> is negative. 175ffe3c632Sopenharmony_ci /// </exception> 176ffe3c632Sopenharmony_ci /// <remarks> 177ffe3c632Sopenharmony_ci /// This will never return an empty <see cref="Span{T}"/>. 178ffe3c632Sopenharmony_ci /// </remarks> 179ffe3c632Sopenharmony_ci /// <remarks> 180ffe3c632Sopenharmony_ci /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. 181ffe3c632Sopenharmony_ci /// </remarks> 182ffe3c632Sopenharmony_ci /// <remarks> 183ffe3c632Sopenharmony_ci /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. 184ffe3c632Sopenharmony_ci /// </remarks> 185ffe3c632Sopenharmony_ci public Span<T> GetSpan(int sizeHint = 0) 186ffe3c632Sopenharmony_ci { 187ffe3c632Sopenharmony_ci CheckAndResizeBuffer(sizeHint); 188ffe3c632Sopenharmony_ci Debug.Assert(_buffer.Length > _index); 189ffe3c632Sopenharmony_ci return _buffer.AsSpan(_index); 190ffe3c632Sopenharmony_ci } 191ffe3c632Sopenharmony_ci 192ffe3c632Sopenharmony_ci private void CheckAndResizeBuffer(int sizeHint) 193ffe3c632Sopenharmony_ci { 194ffe3c632Sopenharmony_ci if (sizeHint < 0) 195ffe3c632Sopenharmony_ci throw new ArgumentException(nameof(sizeHint)); 196ffe3c632Sopenharmony_ci 197ffe3c632Sopenharmony_ci if (sizeHint == 0) 198ffe3c632Sopenharmony_ci { 199ffe3c632Sopenharmony_ci sizeHint = 1; 200ffe3c632Sopenharmony_ci } 201ffe3c632Sopenharmony_ci 202ffe3c632Sopenharmony_ci if (sizeHint > FreeCapacity) 203ffe3c632Sopenharmony_ci { 204ffe3c632Sopenharmony_ci int growBy = Math.Max(sizeHint, _buffer.Length); 205ffe3c632Sopenharmony_ci 206ffe3c632Sopenharmony_ci if (_buffer.Length == 0) 207ffe3c632Sopenharmony_ci { 208ffe3c632Sopenharmony_ci growBy = Math.Max(growBy, DefaultInitialBufferSize); 209ffe3c632Sopenharmony_ci } 210ffe3c632Sopenharmony_ci 211ffe3c632Sopenharmony_ci // enable tests that write to small buffer segments 212ffe3c632Sopenharmony_ci if (MaxGrowBy.HasValue && growBy > MaxGrowBy.Value) 213ffe3c632Sopenharmony_ci { 214ffe3c632Sopenharmony_ci growBy = MaxGrowBy.Value; 215ffe3c632Sopenharmony_ci } 216ffe3c632Sopenharmony_ci 217ffe3c632Sopenharmony_ci int newSize = checked(_buffer.Length + growBy); 218ffe3c632Sopenharmony_ci 219ffe3c632Sopenharmony_ci Array.Resize(ref _buffer, newSize); 220ffe3c632Sopenharmony_ci } 221ffe3c632Sopenharmony_ci 222ffe3c632Sopenharmony_ci Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint); 223ffe3c632Sopenharmony_ci } 224ffe3c632Sopenharmony_ci } 225ffe3c632Sopenharmony_ci}