File: System\Formats\Cbor\Writer\CborWriter.Array.cs
Web Access
Project: src\src\libraries\System.Formats.Cbor\src\System.Formats.Cbor.csproj (System.Formats.Cbor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
 
namespace System.Formats.Cbor
{
    public partial class CborWriter
    {
        // Implements major type 4 encoding per https://tools.ietf.org/html/rfc7049#section-2.1
 
        /// <summary>Writes the start of a definite or indefinite-length array (major type 4).</summary>
        /// <param name="definiteLength">The length of the definite-length array, or <see langword="null" /> for an indefinite-length array.</param>
        /// <exception cref="ArgumentOutOfRangeException">The <paramref name="definiteLength" /> parameter cannot be negative.</exception>
        /// <exception cref="InvalidOperationException"><para>Writing a new value exceeds the definite length of the parent data item.</para>
        /// <para>-or-</para>
        /// <para>The major type of the encoded value is not permitted in the parent data item.</para>
        /// <para>-or-</para>
        /// <para>The written data is not accepted under the current conformance mode.</para></exception>
        /// <remarks>
        /// In canonical conformance modes, the writer will reject indefinite-length writes unless
        /// the <see cref="ConvertIndefiniteLengthEncodings" /> flag is enabled.
        /// </remarks>
        public void WriteStartArray(int? definiteLength)
        {
            if (definiteLength is null)
            {
                WriteStartArrayIndefiniteLength();
            }
            else
            {
                WriteStartArrayDefiniteLength(definiteLength.Value);
            }
        }
 
        /// <summary>Writes the end of an array (major type 4).</summary>
        /// <exception cref="InvalidOperationException"><para>The written data is not accepted under the current conformance mode.</para>
        /// <para>-or-</para>
        /// <para>The definite-length array anticipates more data items.</para></exception>
        public void WriteEndArray()
        {
            PopDataItem(CborMajorType.Array);
            AdvanceDataItemCounters();
        }
 
        private void WriteStartArrayDefiniteLength(int definiteLength)
        {
            if (definiteLength < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(definiteLength));
            }
 
            WriteUnsignedInteger(CborMajorType.Array, (ulong)definiteLength);
            PushDataItem(CborMajorType.Array, definiteLength);
        }
 
        private void WriteStartArrayIndefiniteLength()
        {
            if (!ConvertIndefiniteLengthEncodings && CborConformanceModeHelpers.RequiresDefiniteLengthItems(ConformanceMode))
            {
                throw new InvalidOperationException(SR.Format(SR.Cbor_ConformanceMode_IndefiniteLengthItemsNotSupported, ConformanceMode));
            }
 
            EnsureWriteCapacity(1);
            WriteInitialByte(new CborInitialByte(CborMajorType.Array, CborAdditionalInfo.IndefiniteLength));
            PushDataItem(CborMajorType.Array, definiteLength: null);
        }
 
        // perform an in-place conversion of an indefinite-length encoding into an equivalent definite-length
        private void PatchIndefiniteLengthCollection(CborMajorType majorType, int count)
        {
            Debug.Assert(majorType == CborMajorType.Array || majorType == CborMajorType.Map);
 
            int currentOffset = _offset;
            int bytesToShift = GetIntegerEncodingLength((ulong)count) - 1;
 
            if (bytesToShift > 0)
            {
                // length encoding requires more than 1 byte, need to shift encoded elements to the right
                EnsureWriteCapacity(bytesToShift);
 
                ReadOnlySpan<byte> elementEncoding = _buffer.AsSpan(_frameOffset, currentOffset - _frameOffset);
                Span<byte> target = _buffer.AsSpan(_frameOffset + bytesToShift, currentOffset - _frameOffset);
                elementEncoding.CopyTo(target);
            }
 
            // rewind to the start of the collection and write a new initial byte
            _offset = _frameOffset - 1;
            WriteUnsignedInteger(majorType, (ulong)count);
            _offset = currentOffset + bytesToShift;
        }
    }
}