/// <summary> /// Directly enumerates the contents of BPlusTree from disk in read-only mode. /// </summary> /// <param name="options"> The options normally used to create the <see cref="BPlusTree{TKey, TValue}"/> instance </param> /// <returns> Yields the Key/Value pairs found in the file </returns> public static IEnumerable <KeyValuePair <TKey, TValue> > EnumerateFile(BPlusTreeOptions <TKey, TValue> options) { options = options.Clone(); options.CreateFile = CreatePolicy.Never; options.ReadOnly = true; using (INodeStorage store = options.CreateStorage()) { bool isnew; Node root; IStorageHandle hroot = store.OpenRoot(out isnew); if (isnew) { yield break; } NodeSerializer nodeReader = new NodeSerializer(options, new NodeHandleSerializer(store)); if (isnew || !store.TryGetNode(hroot, out root, nodeReader)) { throw new InvalidDataException(); } Stack <KeyValuePair <Node, int> > todo = new Stack <KeyValuePair <Node, int> >(); todo.Push(new KeyValuePair <Node, int>(root, 0)); while (todo.Count > 0) { KeyValuePair <Node, int> cur = todo.Pop(); if (cur.Value == cur.Key.Count) { continue; } todo.Push(new KeyValuePair <Node, int>(cur.Key, cur.Value + 1)); Node child; if (!store.TryGetNode(cur.Key[cur.Value].ChildNode.StoreHandle, out child, nodeReader)) { throw new InvalidDataException(); } if (child.IsLeaf) { for (int ix = 0; ix < child.Count; ix++) { var set = child[ix].ToKeyValuePair(); var enumerator = set.Value.GetEnumerator(); while (enumerator.MoveNext()) { yield return(new KeyValuePair <TKey, TValue>(set.Key, enumerator.Current.Key)); } } } else { todo.Push(new KeyValuePair <Node, int>(child, 0)); } } } }
public BPlusTree(BPlusTreeOptions <TKey, TValue> ioptions) { bool fileExists = ioptions.StorageType == StorageType.Disk && ioptions.CreateFile != CreatePolicy.Always && File.Exists(ioptions.FileName) && new FileInfo(ioptions.FileName).Length > 0; _options = ioptions.Clone(); _selfLock = _options.CallLevelLock; _keyComparer = _options.KeyComparer; _itemComparer = new ElementComparer(_keyComparer); switch (_options.CachePolicy) { case CachePolicy.All: _storage = new NodeCacheFull(_options); break; case CachePolicy.Recent: _storage = new NodeCacheNormal(_options); break; case CachePolicy.None: _storage = new NodeCacheNone(_options); break; default: throw new InvalidConfigurationValueException("CachePolicy"); } try { _storage.Load(); } catch { _storage.Dispose(); throw; } if (_options.LogFile != null && !_options.ReadOnly) { if (_options.ExistingLogAction == ExistingLogAction.Truncate || _options.ExistingLogAction == ExistingLogAction.Default && !fileExists) { _options.LogFile.TruncateLog(); } else if (_options.LogFile.Size > 0 && ( _options.ExistingLogAction == ExistingLogAction.Replay || _options.ExistingLogAction == ExistingLogAction.ReplayAndCommit || (_options.ExistingLogAction == ExistingLogAction.Default && fileExists) )) { bool commit = (_options.ExistingLogAction == ExistingLogAction.ReplayAndCommit || (_options.ExistingLogAction == ExistingLogAction.Default && fileExists)); bool merge = false; if (_options.StorageType == StorageType.Disk) { merge = new FileInfo(_options.FileName).Length < _options.LogFile.Size; } if (merge) // log data is larger than we are... { BulkInsertOptions opts = new BulkInsertOptions(); opts.CommitOnCompletion = commit; opts.DuplicateHandling = DuplicateHandling.LastValueWins; opts.InputIsSorted = true; opts.ReplaceContents = true; BulkInsert( _options.LogFile.MergeLog(_options.KeyComparer, File.Exists(ioptions.FileName) ? EnumerateFile(ioptions) : new KeyValuePair <TKey, TValue> [0]), opts ); } else { _options.LogFile.ReplayLog(this); if (commit) //Now commit the recovered changes { Commit(); } } } } var nodeStoreWithCount = _storage.Storage as INodeStoreWithCount; if (nodeStoreWithCount != null) { _count = nodeStoreWithCount.Count; _hasCount = _count >= 0; } }