File: Data\SlotCursor.cs
Web Access
Project: src\src\Microsoft.ML.Data\Microsoft.ML.Data.csproj (Microsoft.ML.Data)
// 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.Runtime;
 
namespace Microsoft.ML.Data
{
    /// <summary>
    /// A cursor that allows slot-by-slot access of data. This is to <see cref="ITransposeDataView"/>
    /// what <see cref="DataViewRowCursor"/> is to <see cref="IDataView"/>.
    /// </summary>
    [BestFriend]
    internal abstract class SlotCursor : IDisposable
    {
        [BestFriend]
        private protected readonly IChannel Ch;
        private bool _started;
        protected bool Disposed { get; private set; }
 
        /// <summary>
        /// Whether the cursor is in a state where it can serve up data, that is, <see cref="MoveNext"/>
        /// has been called and returned <see langword="true"/>.
        /// </summary>
        [BestFriend]
        private protected bool IsGood => _started && !Disposed;
 
        [BestFriend]
        private protected SlotCursor(IChannelProvider provider)
        {
            Contracts.AssertValue(provider);
            Ch = provider.Start("Slot Cursor");
        }
 
        /// <summary>
        /// The slot index. Incremented by one when <see cref="MoveNext"/> is called and returns <see langword="true"/>.
        /// When initially created, or after <see cref="MoveNext"/> returns <see langword="false"/>, this will be <c>-1</c>.
        /// </summary>
        public abstract int SlotIndex { get; }
 
        /// <summary>
        /// Advance to the next slot. When the cursor is first created, this method should be called to
        /// move to the first slot. Returns <see langword="false"/> if there are no more slots.
        /// </summary>
        public abstract bool MoveNext();
 
        /// <summary>
        /// The slot type for this cursor. Note that this should equal the
        /// <see cref="ITransposeDataView.GetSlotType"/> for the column from which this slot cursor
        /// was created.
        /// </summary>
        public abstract VectorDataViewType GetSlotType();
 
        /// <summary>
        /// A getter delegate for the slot values. The type <typeparamref name="TValue"/> must correspond
        /// to the item type from <see cref="ITransposeDataView.GetSlotType"/>.
        /// </summary>
        public abstract ValueGetter<VBuffer<TValue>> GetGetter<TValue>();
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            if (Disposed)
                return;
            if (disposing)
                Ch.Dispose();
            Disposed = true;
        }
 
        /// <summary>
        /// For wrapping another slot cursor from which we get <see cref="SlotIndex"/> and <see cref="MoveNext"/>,
        /// but not the data or type accesors. Somewhat analogous to the <see cref="SynchronizedCursorBase"/>
        /// for <see cref="DataViewRowCursor"/>s.
        /// </summary>
        [BestFriend]
        internal abstract class SynchronizedSlotCursor : SlotCursor
        {
            private readonly SlotCursor _root;
 
            public SynchronizedSlotCursor(IChannelProvider provider, SlotCursor cursor)
                : base(provider)
            {
                Contracts.AssertValue(cursor);
                // If the input is itself a sync-base, we can walk up the chain to get its root,
                // thereby making things more efficient.
                _root = cursor is SynchronizedSlotCursor sync ? sync._root : cursor;
            }
 
            public override bool MoveNext()
                => _root.MoveNext();
 
            public override int SlotIndex => _root.SlotIndex;
        }
 
        /// <summary>
        /// A useful base class for common <see cref="SlotCursor"/> implementations, somewhat
        /// analogous to the <see cref="RootCursorBase"/> for <see cref="DataViewRowCursor"/>s.
        /// </summary>
        [BestFriend]
        internal abstract class RootSlotCursor : SlotCursor
        {
            private int _slotIndex;
 
            public RootSlotCursor(IChannelProvider provider)
                : base(provider)
            {
                _slotIndex = -1;
            }
 
            public override int SlotIndex => _slotIndex;
 
            protected sealed override void Dispose(bool disposing)
            {
                if (Disposed)
                    return;
                if (disposing)
                    _slotIndex = -1;
                base.Dispose(disposing);
            }
 
            public override bool MoveNext()
            {
                if (Disposed)
                    return true;
 
                if (MoveNextCore())
                {
                    _slotIndex++;
                    Ch.Assert(_slotIndex >= 0);
                    _started = true;
                    return true;
                }
 
                Dispose();
                Ch.Assert(_slotIndex < 0);
                return false;
            }
 
            /// <summary>
            /// Core implementation of <see cref="MoveNext"/>. This is called only if this method
            /// has not yet previously returned <see langword="false"/>.
            /// </summary>
            protected abstract bool MoveNextCore();
        }
    }
}