File: VBufferEditor.cs
Web Access
Project: src\src\Microsoft.ML.DataView\Microsoft.ML.DataView.csproj (Microsoft.ML.DataView)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using Microsoft.ML.Internal.DataView;
 
namespace Microsoft.ML.Data
{
    /// <summary>
    /// Various methods for creating <see cref="VBufferEditor{T}"/> instances.
    /// </summary>
    public static class VBufferEditor
    {
        /// <summary>
        /// Creates a <see cref="VBufferEditor{T}"/> with the same shape
        /// (length and density) as the <paramref name="destination"/>.
        /// </summary>
        /// <param name="destination">The destination buffer. Note that the resulting <see cref="VBufferEditor{T}"/> is assumed to take ownership
        /// of this passed in object, and so whatever <see cref="VBuffer{T}"/> was passed in as this parameter should not be used again, since its
        /// underlying buffers are being potentially reused.</param>
        public static VBufferEditor<T> CreateFromBuffer<T>(
            scoped ref VBuffer<T> destination)
        {
            return destination.GetEditor();
        }
 
        /// <summary>
        /// Creates a <see cref="VBufferEditor{T}"/> using
        /// <paramref name="destination"/>'s values and indices buffers.
        /// </summary>
        /// <param name="destination">
        /// The destination buffer. Note that the resulting <see cref="VBufferEditor{T}"/> is assumed to take ownership
        /// of this passed in object, and so whatever <see cref="VBuffer{T}"/> was passed in as this parameter should not be used again, since its
        /// underlying buffers are being potentially reused.
        /// </param>
        /// <param name="newLogicalLength">
        /// The logical length of the new buffer being edited.
        /// </param>
        /// <param name="valuesCount">
        /// The optional number of physical values to be represented in the buffer.
        /// The buffer will be dense if <paramref name="valuesCount"/> is omitted.
        /// </param>
        /// <param name="maxValuesCapacity">
        /// The optional number of maximum physical values to represent in the buffer.
        /// The buffer won't grow beyond this maximum size.
        /// </param>
        /// <param name="keepOldOnResize">
        /// True means that the old buffer values and indices are preserved, if possible (Array.Resize is called).
        /// False means that a new array will be allocated, if necessary.
        /// </param>
        /// <param name="requireIndicesOnDense">
        /// True means to ensure the Indices buffer is available, even if the buffer will be dense.
        /// </param>
        public static VBufferEditor<T> Create<T>(
            scoped ref VBuffer<T> destination,
            int newLogicalLength,
            int? valuesCount = null,
            int? maxValuesCapacity = null,
            bool keepOldOnResize = false,
            bool requireIndicesOnDense = false)
        {
            return destination.GetEditor(
                newLogicalLength,
                valuesCount,
                maxValuesCapacity,
                keepOldOnResize,
                requireIndicesOnDense);
        }
    }
 
    /// <summary>
    /// An object capable of editing a <see cref="VBuffer{T}"/> by filling out
    /// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
    /// </summary>
    /// <remarks>
    /// The <see cref="VBuffer{T}"/> structure by itself is immutable. However, the purpose of <see cref="VBuffer{T}"/>
    /// is to enable buffer re-use we can edit them through this structure, as created through
    /// <see cref="VBufferEditor.Create{T}(ref VBuffer{T}, int, int?, int?, bool, bool)"/> or
    /// <see cref="VBufferEditor.CreateFromBuffer{T}(ref VBuffer{T})"/>.
    /// </remarks>
    public readonly ref struct VBufferEditor<T>
    {
        private readonly int _logicalLength;
        private readonly T[] _values;
        private readonly int[] _indices;
 
        /// <summary>
        /// The mutable span of values.
        /// </summary>
        public readonly Span<T> Values;
 
        /// <summary>
        /// The mutable span of indices.
        /// </summary>
        public readonly Span<int> Indices;
 
        /// <summary>
        /// Gets a value indicating whether a new <see cref="Values"/> array was allocated.
        /// </summary>
        public bool CreatedNewValues { get; }
 
        /// <summary>
        /// Gets a value indicating whether a new <see cref="Indices"/> array was allocated.
        /// </summary>
        public bool CreatedNewIndices { get; }
 
        internal VBufferEditor(int logicalLength,
            int physicalValuesCount,
            T[] values,
            int[] indices,
            bool requireIndicesOnDense,
            bool createdNewValues,
            bool createdNewIndices)
        {
            _logicalLength = logicalLength;
            _values = values;
            _indices = indices;
 
            bool isDense = logicalLength == physicalValuesCount;
 
            Values = _values.AsSpan(0, physicalValuesCount);
            Indices = !isDense || requireIndicesOnDense ? _indices.AsSpan(0, physicalValuesCount) : default;
 
            CreatedNewValues = createdNewValues;
            CreatedNewIndices = createdNewIndices;
        }
 
        /// <summary>
        /// Commits the edits and creates a new <see cref="VBuffer{T}"/> using the current <see cref="Values"/> and <see cref="Indices"/>.
        /// Note that this structure and its properties should not be used once this is called.
        /// </summary>
        /// <returns>The newly created <see cref="VBuffer{T}"/>.</returns>
        public VBuffer<T> Commit()
        {
            return new VBuffer<T>(_logicalLength, Values.Length, _values, _indices);
        }
 
        /// <summary>
        /// Commits the edits and creates a new <see cref="VBuffer{T}"/> using
        /// the current Values and Indices, while allowing to truncate the length
        /// of <see cref="Values"/> and, if sparse, <see cref="Indices"/>.
        /// Like <see cref="Commit"/>, this structure and its properties should not be used once this is called.
        /// </summary>
        /// <param name="physicalValuesCount">
        /// The new number of physical values to be represented in the created buffer.
        /// </param>
        /// <returns>
        /// The newly created <see cref="VBuffer{T}"/>.
        /// </returns>
        /// <remarks>
        /// This method allows to modify the length of the explicitly defined values.
        /// This is useful in sparse situations where the <see cref="VBufferEditor{T}"/>
        /// was created with a larger physical value count than was needed
        /// because the final value count was not known at creation time.
        /// </remarks>
        public VBuffer<T> CommitTruncated(int physicalValuesCount)
        {
            Contracts.CheckParam(physicalValuesCount <= Values.Length, nameof(physicalValuesCount),
                "Updating " + nameof(physicalValuesCount) + " during " + nameof(CommitTruncated) +
                " cannot be greater than the original physicalValuesCount value used in " + nameof(VBufferEditor.Create) + ".");
 
            return new VBuffer<T>(_logicalLength, physicalValuesCount, _values, _indices);
        }
    }
}