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}