// when we can't use code generation, we can use these methods
        internal static T[] Clone1DimArraySafeInternal <T>(T[] obj, DeepCloneState state)
        {
            var l        = obj.Length;
            var outArray = new T[l];

            state.AddKnownRef(obj, outArray);
            Array.Copy(obj, outArray, obj.Length);
            return(outArray);
        }
        internal static T[] Clone1DimArrayClassInternal <T>(T[] obj, DeepCloneState state)
        {
            // not null from called method, but will check it anyway
            if (obj == null)
            {
                return(null);
            }
            var l        = obj.Length;
            var outArray = new T[l];

            state.AddKnownRef(obj, outArray);
            for (var i = 0; i < l; i++)
            {
                outArray[i] = (T)CloneClassInternal(obj[i], state);
            }

            return(outArray);
        }
        // relatively frequent case. specially handled
        internal static T[,] Clone2DimArrayInternal <T>(T[,] obj, DeepCloneState state)
        {
            // not null from called method, but will check it anyway
            if (obj == null)
            {
                return(null);
            }
            var l1       = obj.GetLength(0);
            var l2       = obj.GetLength(1);
            var outArray = new T[l1, l2];

            state.AddKnownRef(obj, outArray);
            if (DeepClonerSafeTypes.CanReturnSameObject(typeof(T)))
            {
                Array.Copy(obj, outArray, obj.Length);
                return(outArray);
            }

            if (typeof(T).GetTypeInfo().IsValueType)
            {
                var cloner = GetClonerForValueType <T>();
                for (var i = 0; i < l1; i++)
                {
                    for (var k = 0; k < l2; k++)
                    {
                        outArray[i, k] = cloner(obj[i, k], state);
                    }
                }
            }
            else
            {
                for (var i = 0; i < l1; i++)
                {
                    for (var k = 0; k < l2; k++)
                    {
                        outArray[i, k] = (T)CloneClassInternal(obj[i, k], state);
                    }
                }
            }

            return(outArray);
        }
        internal static T[] Clone1DimArrayStructInternal <T>(T[] obj, DeepCloneState state)
        {
            // not null from called method, but will check it anyway
            if (obj == null)
            {
                return(null);
            }
            var l        = obj.Length;
            var outArray = new T[l];

            state.AddKnownRef(obj, outArray);
            var cloner = GetClonerForValueType <T>();

            for (var i = 0; i < l; i++)
            {
                outArray[i] = cloner(obj[i], state);
            }

            return(outArray);
        }
        // rare cases, very slow cloning. currently it's ok
        internal static Array CloneAbstractArrayInternal(Array obj, DeepCloneState state)
        {
            // not null from called method, but will check it anyway
            if (obj == null)
            {
                return(null);
            }
            var rank = obj.Rank;

            var lowerBounds = Enumerable.Range(0, rank).Select(obj.GetLowerBound).ToArray();
            var lengths     = Enumerable.Range(0, rank).Select(obj.GetLength).ToArray();
            var idxes       = Enumerable.Range(0, rank).Select(obj.GetLowerBound).ToArray();

            var outArray = Array.CreateInstance(obj.GetType().GetElementType(), lengths, lowerBounds);

            state.AddKnownRef(obj, outArray);
            while (true)
            {
                outArray.SetValue(CloneClassInternal(obj.GetValue(idxes), state), idxes);
                var ofs = rank - 1;
                while (true)
                {
                    idxes[ofs]++;
                    if (idxes[ofs] >= lowerBounds[ofs] + lengths[ofs])
                    {
                        idxes[ofs] = lowerBounds[ofs];
                        ofs--;
                        if (ofs < 0)
                        {
                            return(outArray);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }