Beispiel #1
0
        private void CtorTestImplementation(
            MultidimensionalCube2 <int> cube, ref int[] dimensionValuesCountLimits,
            ref int factoryCallsCount,
            ref object[] lastFactoryCall,
            string suffix,
            int point,
            int totalPointsCountLimit)
        {
            Assert.IsNotNull(cube);

            Assert.AreEqual(dimensionValuesCountLimits.Length, cube.DimensionsCount);

            for (int d = 0; d < dimensionValuesCountLimits.Length; d++)
            {
                int limit = dimensionValuesCountLimits[d];
                Assert.AreEqual(limit, cube.GetDimensionValuesCountLimit(d));
            }

            Assert.AreEqual(totalPointsCountLimit, cube.TotalPointsCountLimit);

            string[] newPointVector = new string[] { $"A{suffix}", $"B{suffix}", $"C{suffix}", $"D{suffix}" };
            MultidimensionalPointResult <int> result = cube.TryGetOrCreatePoint(newPointVector);

            Assert.IsTrue(result.IsSuccess);
            Assert.AreEqual(point, result.Point);

            Util.AssertAreEqual(newPointVector, lastFactoryCall);
        }
        /// <summary>@ToDo: Complete documentation before stable release. {444}.</summary>
        /// <param name="coordinates">@ToDo: Complete documentation before stable release. {769}.</param>
        /// <returns>@ToDo: Complete documentation before stable release. {229}.</returns>
        public MultidimensionalPointResult <TPoint> TryGetOrCreatePoint(params string[] coordinates)
        {
            string pointMoniker = this.GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = this.points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            if (this.totalPointsCount >= this.totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            this.pointCreationLock.Wait();
            try
            {
                MultidimensionalPointResult <TPoint> result = this.TryCreatePoint(coordinates, pointMoniker);
                return(result);
            }
            finally
            {
                this.pointCreationLock.Release();
            }
        }
        /// <summary>@ToDo: Complete documentation before stable release. {969}.</summary>
        /// <param name="cancelToken">@ToDo: Complete documentation before stable release. {844}.</param>
        /// <param name="coordinates">@ToDo: Complete documentation before stable release. {472}.</param>
        /// <returns>@ToDo: Complete documentation before stable release. {660}.</returns>
        public async Task <MultidimensionalPointResult <TPoint> > TryGetOrCreatePointAsync(CancellationToken cancelToken, params string[] coordinates)
        {
            string pointMoniker = this.GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = this.points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            if (this.totalPointsCount >= this.totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            await this.pointCreationLock.WaitAsync(cancelToken).ConfigureAwait(continueOnCapturedContext: false);

            try
            {
                MultidimensionalPointResult <TPoint> result = this.TryCreatePoint(coordinates, pointMoniker);
                return(result);
            }
            finally
            {
                this.pointCreationLock.Release();
            }
        }
        public MultidimensionalPointResult <TPoint> TryGetVector(TDimensionValue[] coordinates)
        {
            Util.ValidateNotNull(coordinates, nameof(coordinates));

            if (coordinates.Length != _ownerCube.DimensionsCount)
            {
                throw new ArgumentException(
                          $"The specified {nameof(coordinates)}-vector has {coordinates.Length} dimensions."
                          + $" However {nameof(_ownerCube)} has {_ownerCube.DimensionsCount} dimensions.",
                          nameof(coordinates));
            }

            MultidimensionalPointResult <TPoint> result = this.TryGetOrAddVectorInternal(coordinates, currentDim: 0, createIfNotExists: false);

            return(result);
        }
        /// <summary>@ToDo: Complete documentation before stable release. {477}.</summary>
        /// <param name="coordinates">@ToDo: Complete documentation before stable release. {344}.</param>
        /// <returns>@ToDo: Complete documentation before stable release. {444}.</returns>
        public MultidimensionalPointResult <TPoint> TryGetPoint(params string[] coordinates)
        {
            string pointMoniker = this.GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = this.points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }
            else
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_PointDoesNotExistCreationNotRequested, -1);
                return(result);
            }
        }
Beispiel #6
0
        private MultidimensionalPointResult <TPoint> TryGetOrAddVectorInternal(TDimensionValue[] coordinates, int currentDim, bool createIfNotExists)
        {
            TDimensionValue subElementKey = coordinates[currentDim];

            // Try and get the referenced element:
            object subElement;
            bool   subElementExists = this.elements.TryGetValue(subElementKey, out subElement);

            // If the referenced element exists, we can simply proceed:
            if (subElementExists)
            {
                if (this.isLastDimensionLevel)
                {
                    var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, (TPoint)subElement);
                    return(result);
                }
                else
                {
                    MultidimensionalCubeDimension <TDimensionValue, TPoint> subDim = (MultidimensionalCubeDimension <TDimensionValue, TPoint>)subElement;
                    MultidimensionalPointResult <TPoint> result = subDim.TryGetOrAddVectorInternal(coordinates, currentDim + 1, createIfNotExists);
                    return(result);
                }
            }
            else
            {
                // so - subElement does NOT exist:
                // If we are not to create new elements, we are done:
                if (false == createIfNotExists)
                {
                    var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_PointDoesNotExistCreationNotRequested, currentDim);
                    return(result);
                }
                else
                {
                    MultidimensionalPointResult <TPoint> result = this.isLastDimensionLevel
                                                        ? this.TryAddPoint(coordinates, currentDim)
                                                        : this.TryAddSubvector(coordinates, currentDim);
                    return(result);
                }
            }
        }
        private MultidimensionalPointResult <TPoint> TryCreatePoint(string[] coordinates, string pointMoniker)
        {
#pragma warning disable SA1509 // Opening braces must not be preceded by blank line

            // We already have tried getting the existing point and failed.
            // We also checked that _totalPointsCountLimit was not reached (outside the lock).
            // Lastly, we took a lock.
            // Now we can begin the slow path.

            // First, we need to try retrieving the point again, now under the lock:
            TPoint point;
            bool   hasPoint = this.points.TryGetValue(pointMoniker, out point);
            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            // Then, check total count again now that we are under lock:
            if (this.totalPointsCount >= this.totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            // Examine each dimension and see if it reached values count limit. If not, track the new value:

            int      reachedValsLimitDim = -1;
            BitArray valueAddedToDims    = new BitArray(length: coordinates.Length, defaultValue: false);

            for (int i = 0; i < coordinates.Length; i++)
            {
                HashSet <string> dimVals       = this.dimensionValues[i];
                string           coordinateVal = coordinates[i];

                if ((dimVals.Count >= this.dimensionValuesCountLimits[i]) && (false == dimVals.Contains(coordinateVal)))
                {
                    reachedValsLimitDim = i;
                    break;
                }

                bool added = dimVals.Add(coordinates[i]);
                valueAddedToDims.Set(i, added);
            }

            // We hit the _dimensionValuesCountLimits at some dimension.
            // Remove what we just added to dim value sets and give up.

            if (reachedValsLimitDim != -1)
            {
                for (int i = 0; i <= reachedValsLimitDim; i++)
                {
                    if (valueAddedToDims.Get(i))
                    {
                        this.dimensionValues[i].Remove(coordinates[i]);
                    }
                }

                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_SubdimensionsCountLimitReached, reachedValsLimitDim);
                return(result);
            }

            // Create new point:

            try
            {
                point = this.pointsFactory(coordinates);
            }
            catch (Exception ex)
            {
                // User code in _pointsFactory may throw. In that case we need to clean up from the added value containers:
                for (int i = 0; i <= reachedValsLimitDim; i++)
                {
                    if (valueAddedToDims.Get(i))
                    {
                        this.dimensionValues[i].Remove(coordinates[i]);
                    }
                }

                ExceptionDispatchInfo.Capture(ex).Throw();
                throw;  // This line will never be reached
            }

            {
                bool added = this.points.TryAdd(pointMoniker, point);
                if (false == added)
                {
                    throw new InvalidOperationException(Invariant($"Internal SDK bug. Please report this! (pointMoniker: {pointMoniker})")
                                                        + Invariant($" Info: Failed to add a point to the {nameof(this.points)}-collection in")
                                                        + Invariant($" class {nameof(MultidimensionalCube2<TPoint>)} despite passing all the cerfification checks."));
                }
            }

            // Inc total points count.
            this.totalPointsCount++;

            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_NewPointCreated, point);
                return(result);
            }
#pragma warning restore SA1509 // Opening braces must not be preceded by blank line
        }
        public MultidimensionalPointResult <TPoint> TryGetPoint(params TDimensionValue[] coordinates)
        {
            MultidimensionalPointResult <TPoint> result = this.points.TryGetVector(coordinates);

            return(result);
        }
        private MultidimensionalPointResult <TPoint> TryAddSubvector(TDimensionValue[] coordinates, int currentDim)
        {
            // Note the comment near the top if TryAddPoint(..) about the applied minimal locking strategy.

            // Check if we reached the dimensions count limit. If we did, we give up. Otherwise we start tracking whether we need to undo the increment later:
            if (!this.TryIncSubdimensionsCount())
            {
                return(new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_SubdimensionsCountLimitReached, currentDim));
            }

            bool mustRestoreSubdimensionsCount = true;

            try
            {
                TDimensionValue subElementKey = coordinates[currentDim];

                // Do a soft-check to see if we reached the total points limit. If we do, there is no need to bother:
                // (We will do a hard check and pre-booking later when we actually about to create the point.)
                if (_ownerCube.TotalPointsCount >= _ownerCube.TotalPointsCountLimit)
                {
                    return(new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, failureCoordinateIndex: -1));
                }

                // We are not at the last level. Create the subdimension. Note, we are not under lock, so someone might be creating the same dimention concurrently:
                int  nextDim = currentDim + 1;
                bool isLastDimensionLevel = (nextDim == coordinates.Length - 1);
                var  newSubDim            = new MultidimensionalCubeDimension <TDimensionValue, TPoint>(
                    _ownerCube,
                    _ownerCube.GetSubdimensionsCountLimit(nextDim),
                    isLastDimensionLevel);
                MultidimensionalPointResult <TPoint> newSubDimResult = newSubDim.TryGetOrAddVectorInternal(coordinates, nextDim, createIfNotExists: true);

                // Becasue we have not yet inserted newSubDim into _elements, any operations on newSubDim are not under concurrency.
                // There are no point-vectors yet pointing to the sub-space of newSubDim, so no DimensionValuesCountLimit can be reached.
                // So, hasNewPoint can be false only if TotalPointsCountLimit was reached. We just bail out:
                if (!newSubDimResult.IsSuccess)
                {
                    return(newSubDimResult);
                }

                // The new point has been created and we need to add its sub-space to the list.
                // However, there is a race on someone calling GetOrAddVector(..) with the same coordinates. So newSubDim may or may not already be in the list.
                bool couldInsert = _elements.TryAdd(subElementKey, newSubDim);

                if (couldInsert)
                {
                    // Success. We created and inserted a new sub-space:
                    mustRestoreSubdimensionsCount = false;
                    return(newSubDimResult);
                }
                else
                {
                    // The point was already in the list. It's that other point (created by the race winner) that we want.
                    // We need to discard the sub-space and the point we just created: Decrement total points count and Decrement the dimension value count.
                    // After that we just call TryGetOrAddVectorInternal(..) again on the same recursion level.
                    _ownerCube.DecTotalPointsCount();
                }
            }
            finally
            {
                if (mustRestoreSubdimensionsCount)
                {
                    this.DecSubdimensionsCount();
                }
            }

            MultidimensionalPointResult <TPoint> retryResult = this.TryGetOrAddVectorInternal(coordinates, currentDim, createIfNotExists: true);

            return(retryResult);
        }
Beispiel #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        public MultidimensionalPointResult <TPoint> TryGetOrCreatePoint(params TDimensionValue[] coordinates)
        {
            MultidimensionalPointResult <TPoint> result = _points.TryGetOrAddVector(coordinates);

            return(result);
        }