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); } } }
public MultidimensionalCube(int totalPointsCountLimit, Func <TDimensionValue[], TPoint> pointsFactory, params int[] subdimensionsCountLimits) { if (totalPointsCountLimit < 1) { throw new ArgumentOutOfRangeException(nameof(totalPointsCountLimit), Invariant($"{nameof(totalPointsCountLimit)} must be 1 or larger. Typically much larger.")); } Util.ValidateNotNull(pointsFactory, nameof(pointsFactory)); Util.ValidateNotNull(subdimensionsCountLimits, nameof(subdimensionsCountLimits)); if (subdimensionsCountLimits.Length == 0) { throw new ArgumentException("Cube must have 1 or more dimensions.", nameof(subdimensionsCountLimits)); } if (subdimensionsCountLimits.Length > DimensionsCountLimit) { throw new ArgumentException(Invariant($"Cube may not have more than ${MultidimensionalCube<TDimensionValue, TPoint>.DimensionsCountLimit} dimensions,") + Invariant($" but {subdimensionsCountLimits.Length} dimensions were specified.")); } for (int d = 0; d < subdimensionsCountLimits.Length; d++) { if (subdimensionsCountLimits[d] < 1) { throw new ArgumentException(Invariant($"The limit of distinct dimension values must be 1 or larger, but the limit specified for dimension {d} is {subdimensionsCountLimits[d]}.")); } } this.totalPointsCountLimit = totalPointsCountLimit; this.subdimensionsCountLimits = subdimensionsCountLimits; this.points = new MultidimensionalCubeDimension <TDimensionValue, TPoint>(this, subdimensionsCountLimits[0], subdimensionsCountLimits.Length == 1); this.pointsFactory = pointsFactory; }
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); }