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.IO;
36ffe3c632Sopenharmony_ciusing System.Runtime.CompilerServices;
37ffe3c632Sopenharmony_ciusing System.Security;
38ffe3c632Sopenharmony_ci
39ffe3c632Sopenharmony_cinamespace Google.Protobuf
40ffe3c632Sopenharmony_ci{
41ffe3c632Sopenharmony_ci    /// <summary>
42ffe3c632Sopenharmony_ci    /// Abstraction for writing to a steam / IBufferWriter
43ffe3c632Sopenharmony_ci    /// </summary>
44ffe3c632Sopenharmony_ci    [SecuritySafeCritical]
45ffe3c632Sopenharmony_ci    internal struct WriteBufferHelper
46ffe3c632Sopenharmony_ci    {
47ffe3c632Sopenharmony_ci        private IBufferWriter<byte> bufferWriter;
48ffe3c632Sopenharmony_ci        private CodedOutputStream codedOutputStream;
49ffe3c632Sopenharmony_ci
50ffe3c632Sopenharmony_ci        public CodedOutputStream CodedOutputStream => codedOutputStream;
51ffe3c632Sopenharmony_ci
52ffe3c632Sopenharmony_ci        /// <summary>
53ffe3c632Sopenharmony_ci        /// Initialize an instance with a coded output stream.
54ffe3c632Sopenharmony_ci        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
55ffe3c632Sopenharmony_ci        /// and we can write directly into it without copying.
56ffe3c632Sopenharmony_ci        /// </summary>
57ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
58ffe3c632Sopenharmony_ci        public static void Initialize(CodedOutputStream codedOutputStream, out WriteBufferHelper instance)
59ffe3c632Sopenharmony_ci        {
60ffe3c632Sopenharmony_ci            instance.bufferWriter = null;
61ffe3c632Sopenharmony_ci            instance.codedOutputStream = codedOutputStream;
62ffe3c632Sopenharmony_ci        }
63ffe3c632Sopenharmony_ci
64ffe3c632Sopenharmony_ci        /// <summary>
65ffe3c632Sopenharmony_ci        /// Initialize an instance with a buffer writer.
66ffe3c632Sopenharmony_ci        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
67ffe3c632Sopenharmony_ci        /// and we can write directly into it without copying.
68ffe3c632Sopenharmony_ci        /// </summary>
69ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
70ffe3c632Sopenharmony_ci        public static void Initialize(IBufferWriter<byte> bufferWriter, out WriteBufferHelper instance, out Span<byte> buffer)
71ffe3c632Sopenharmony_ci        {
72ffe3c632Sopenharmony_ci            instance.bufferWriter = bufferWriter;
73ffe3c632Sopenharmony_ci            instance.codedOutputStream = null;
74ffe3c632Sopenharmony_ci            buffer = default;  // TODO: initialize the initial buffer so that the first write is not via slowpath.
75ffe3c632Sopenharmony_ci        }
76ffe3c632Sopenharmony_ci
77ffe3c632Sopenharmony_ci        /// <summary>
78ffe3c632Sopenharmony_ci        /// Initialize an instance with a buffer represented by a single span (i.e. buffer cannot be refreshed)
79ffe3c632Sopenharmony_ci        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
80ffe3c632Sopenharmony_ci        /// and we can write directly into it without copying.
81ffe3c632Sopenharmony_ci        /// </summary>
82ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
83ffe3c632Sopenharmony_ci        public static void InitializeNonRefreshable(out WriteBufferHelper instance)
84ffe3c632Sopenharmony_ci        {
85ffe3c632Sopenharmony_ci            instance.bufferWriter = null;
86ffe3c632Sopenharmony_ci            instance.codedOutputStream = null;
87ffe3c632Sopenharmony_ci        }
88ffe3c632Sopenharmony_ci
89ffe3c632Sopenharmony_ci        /// <summary>
90ffe3c632Sopenharmony_ci        /// Verifies that SpaceLeft returns zero.
91ffe3c632Sopenharmony_ci        /// </summary>
92ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
93ffe3c632Sopenharmony_ci        public static void CheckNoSpaceLeft(ref WriterInternalState state)
94ffe3c632Sopenharmony_ci        {
95ffe3c632Sopenharmony_ci            if (GetSpaceLeft(ref state) != 0)
96ffe3c632Sopenharmony_ci            {
97ffe3c632Sopenharmony_ci                throw new InvalidOperationException("Did not write as much data as expected.");
98ffe3c632Sopenharmony_ci            }
99ffe3c632Sopenharmony_ci        }
100ffe3c632Sopenharmony_ci
101ffe3c632Sopenharmony_ci        /// <summary>
102ffe3c632Sopenharmony_ci        /// If writing to a flat array, returns the space left in the array. Otherwise,
103ffe3c632Sopenharmony_ci        /// throws an InvalidOperationException.
104ffe3c632Sopenharmony_ci        /// </summary>
105ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
106ffe3c632Sopenharmony_ci        public static int GetSpaceLeft(ref WriterInternalState state)
107ffe3c632Sopenharmony_ci        {
108ffe3c632Sopenharmony_ci            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream == null && state.writeBufferHelper.bufferWriter == null)
109ffe3c632Sopenharmony_ci            {
110ffe3c632Sopenharmony_ci                return state.limit - state.position;
111ffe3c632Sopenharmony_ci            }
112ffe3c632Sopenharmony_ci            else
113ffe3c632Sopenharmony_ci            {
114ffe3c632Sopenharmony_ci                throw new InvalidOperationException(
115ffe3c632Sopenharmony_ci                    "SpaceLeft can only be called on CodedOutputStreams that are " +
116ffe3c632Sopenharmony_ci                        "writing to a flat array or when writing to a single span.");
117ffe3c632Sopenharmony_ci            }
118ffe3c632Sopenharmony_ci        }
119ffe3c632Sopenharmony_ci
120ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.NoInlining)]
121ffe3c632Sopenharmony_ci        public static void RefreshBuffer(ref Span<byte> buffer, ref WriterInternalState state)
122ffe3c632Sopenharmony_ci        {
123ffe3c632Sopenharmony_ci            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream != null)
124ffe3c632Sopenharmony_ci            {
125ffe3c632Sopenharmony_ci                // because we're using coded output stream, we know that "buffer" and codedOutputStream.InternalBuffer are identical.
126ffe3c632Sopenharmony_ci                state.writeBufferHelper.codedOutputStream.InternalOutputStream.Write(state.writeBufferHelper.codedOutputStream.InternalBuffer, 0, state.position);
127ffe3c632Sopenharmony_ci                // reset position, limit stays the same because we are reusing the codedOutputStream's internal buffer.
128ffe3c632Sopenharmony_ci                state.position = 0;
129ffe3c632Sopenharmony_ci            }
130ffe3c632Sopenharmony_ci            else if (state.writeBufferHelper.bufferWriter != null)
131ffe3c632Sopenharmony_ci            {
132ffe3c632Sopenharmony_ci                // commit the bytes and get a new buffer to write to.
133ffe3c632Sopenharmony_ci                state.writeBufferHelper.bufferWriter.Advance(state.position);
134ffe3c632Sopenharmony_ci                state.position = 0;
135ffe3c632Sopenharmony_ci                buffer = state.writeBufferHelper.bufferWriter.GetSpan();
136ffe3c632Sopenharmony_ci                state.limit = buffer.Length;
137ffe3c632Sopenharmony_ci            }
138ffe3c632Sopenharmony_ci            else
139ffe3c632Sopenharmony_ci            {
140ffe3c632Sopenharmony_ci                // We're writing to a single buffer.
141ffe3c632Sopenharmony_ci                throw new CodedOutputStream.OutOfSpaceException();
142ffe3c632Sopenharmony_ci            }
143ffe3c632Sopenharmony_ci        }
144ffe3c632Sopenharmony_ci
145ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
146ffe3c632Sopenharmony_ci        public static void Flush(ref Span<byte> buffer, ref WriterInternalState state)
147ffe3c632Sopenharmony_ci        {
148ffe3c632Sopenharmony_ci            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream != null)
149ffe3c632Sopenharmony_ci            {
150ffe3c632Sopenharmony_ci                // because we're using coded output stream, we know that "buffer" and codedOutputStream.InternalBuffer are identical.
151ffe3c632Sopenharmony_ci                state.writeBufferHelper.codedOutputStream.InternalOutputStream.Write(state.writeBufferHelper.codedOutputStream.InternalBuffer, 0, state.position);
152ffe3c632Sopenharmony_ci                state.position = 0;
153ffe3c632Sopenharmony_ci            }
154ffe3c632Sopenharmony_ci            else if (state.writeBufferHelper.bufferWriter != null)
155ffe3c632Sopenharmony_ci            {
156ffe3c632Sopenharmony_ci                // calling Advance invalidates the current buffer and we must not continue writing to it,
157ffe3c632Sopenharmony_ci                // so we set the current buffer to point to an empty Span. If any subsequent writes happen,
158ffe3c632Sopenharmony_ci                // the first subsequent write will trigger refresing of the buffer.
159ffe3c632Sopenharmony_ci                state.writeBufferHelper.bufferWriter.Advance(state.position);
160ffe3c632Sopenharmony_ci                state.position = 0;
161ffe3c632Sopenharmony_ci                state.limit = 0;
162ffe3c632Sopenharmony_ci                buffer = default;  // invalidate the current buffer
163ffe3c632Sopenharmony_ci            }
164ffe3c632Sopenharmony_ci        }
165ffe3c632Sopenharmony_ci    }
166ffe3c632Sopenharmony_ci}