/// <summary>
 /// Loads initial cache data
 /// </summary>
 /// <param name="initialLoader"></param>
 /// <param name="cfg"></param>
 private void LoadData(ICacheLoadProvider <TValue, TKey> initialLoader, Config <TValue, TKey> cfg)
 {
     if (initialLoader != null)
     {
         var intLoadTimeMaxSecs = cfg.LoadCacheMaxSeconds;
         var numberOfSets       = this.Config.NumberOfSets;
         var itemsPerSet        = this.Config.ItemsPerSet;
         var arrSetIDXs         = new int[this.Config.NumberOfSets];
         arrSetIDXs[0] = -1;
         int      totalNumberLoaded     = 0;
         int      totalNumberOfFullSets = 0;
         DateTime start = DateTime.UtcNow;
         while (initialLoader.GetNextKey(totalNumberLoaded, cfg,
                                         (iSet) => {
             return((iSet > -1 && iSet < numberOfSets) ? arrSetIDXs[iSet] : -1);
         },
                                         out TKey key))
         {
             int setNumber = FindSetNumber(key, out CacheItem <TValue, TKey>[] d);
             if (setNumber > -1)
             {
                 var idxMax = arrSetIDXs[setNumber];
                 if ((idxMax == 0 && setNumber > 0) ||
                     (idxMax == -1 && setNumber == 0) ||
                     (idxMax > 0 && (idxMax < itemsPerSet - 1)))   //Check if this set is not full
                 {
                     TValue val = initialLoader.GetValue(key);
                     if (idxMax < 0)
                     {
                         idxMax = 0;
                     }
                     d[idxMax + this.SetNumberToSetAddress(setNumber)] = m_itemCreateHr(val, key);
                     arrSetIDXs[setNumber] = idxMax + 1;
                     totalNumberLoaded    += 1;
                     if (idxMax >= (itemsPerSet - 1))
                     {
                         totalNumberOfFullSets += 1;
                     }
                 }
                 if ((totalNumberOfFullSets >= numberOfSets) ||
                     (intLoadTimeMaxSecs > 0 && (DateTime.UtcNow - start).TotalSeconds >= intLoadTimeMaxSecs))
                 {
                     break; //finish when all sets are full or the time elapsed is greater than a limit set by Config
                 }
             }
         }
     }
 }
 /// <summary>
 /// Constructs new cache object for a given <see cref="Config{TValue, TKey}"/> and initialLoader object.
 /// </summary>
 /// <param name="config">Cache configuration obect to be used for cache construction purposes.</param>
 /// <param name="initialLoader">Initial loader implementation to be used for cache initialization</param>
 public Cache(Config <TValue, TKey> config, ICacheLoadProvider <TValue, TKey> initialLoader) : base()
 {
     if (config == null)
     {
         throw new ArgumentException("config argument is reguired.");
     }
     this.Config = config;
     if (config.Strategy == ReplacementStrategy.Custom && (config.ReplacementHandler != null))
     {
         m_replacementHandler = config.ReplacementHandler;
     }
     m_cachePlus  = new CacheItem <TValue, TKey> [config.Size - 1];
     m_cacheMinus = new CacheItem <TValue, TKey> [config.Size - 1];
     if (config.CacheType != CachingAlgorithm.FullyAssociative)
     {
         m_SetAddressMask = (uint)(2 << config.SetSizeBits) - 1;
     }
     m_HashCalc       = config.HashCalculator ?? DefaultCalcHashFunc;
     m_itemCreateHr   = config.CreateItemHandler ?? DefaultItemCreateHr;
     m_LoadHandler    = config.LoadHandler;
     m_MaxScanThreads = (config.ThreadsMaxNumberOfScan > 0)? config.ThreadsMaxNumberOfScan : Config <TValue, TKey> .DefaultMaxNumberOfScanThreads;
     LoadData(initialLoader, config);
 }