// Perform a lookup in the b-tree private static TreeNode LookupTreeNode(TreeIntermediate parent, UInt64 key, Action <TreeIntermediate> readDeferred) { if (parent.ReadDeferred) { if (readDeferred != null) { readDeferred(parent); } else { throw new Exception("Deferred index found, but no reader supplied"); } } TreeIntermediate next = null; foreach (var n in parent.Children) { if (key > n.Key) { next = n as TreeIntermediate; } else if (key < n.Key) { if (next != null) { return(LookupTreeNode(next, key, readDeferred)); } else { return(null); // Key does not exist } } else // key matches { if (n is TreeIntermediate) { return(LookupTreeNode((TreeIntermediate)n, key, readDeferred)); } else { return(n); } } } if (next != null) { return(LookupTreeNode(next, key, readDeferred)); } else { return(null); // Key does not exist } }
// When a data block has a subnode, it can be a simple node, or a two-level tree // This reads a sub node and builds suitable data structures, so that we can later access data held in it private void ReadSubNodeBtreeUnicode(FileStream fs, UInt64 subDataBid, TreeIntermediate parent) { var rb = LookupDataBlock(subDataBid); if (rb == null) { throw new XstException("SubNode data block does not exist"); } int read; byte[] buffer = ReadAndDecompress(fs, rb, out read); var sl = Map.MapType <SLBLOCKUnicode>(buffer); if (sl.cLevel > 0) { var rgbid = Map.MapArray <SIENTRYUnicode>(buffer, Marshal.SizeOf(typeof(SLBLOCKUnicode)), sl.cEnt); foreach (var sie in rgbid) { var inter = new TreeIntermediate { Key = (sie.nid & 0xffffffff) }; parent.Children.Add(inter); // Read child nodes in tree ReadSubNodeBtreeUnicode(fs, sie.bid, inter); } } else { var rgbid = Map.MapArray <SLENTRYUnicode>(buffer, Marshal.SizeOf(typeof(SLBLOCKUnicode)), sl.cEnt); foreach (var sle in rgbid) { // Only use low order dword of nid var nb = new Node { Key = (sle.nid & 0xffffffff), DataBid = sle.bidData, SubDataBid = sle.bidSub }; parent.Children.Add(nb); } } }
// A callback to be used when searching a tree to read part of the index whose loading has been deferred private void ReadDeferredIndex(TreeIntermediate inter) { using (var fs = GetReadStream()) { if (IsUnicode4K) { ReadBTPageUnicode4K(fs, (ulong)inter.fileOffset, inter); } else if (IsUnicode) { ReadBTPageUnicode(fs, (ulong)inter.fileOffset, inter); } else { ReadBTPageANSI(fs, (ulong)inter.fileOffset, inter); } // Don't read it again inter.fileOffset = null; } }
private void ReadBTPageANSI(FileStream fs, ulong fileOffset, TreeIntermediate parent) { fs.Seek((long)fileOffset, SeekOrigin.Begin); var p = Map.ReadType <BTPAGEANSI>(fs); // read entries for (int i = 0; i < p.cEnt; i++) { if (p.cLevel > 0) { BTENTRYANSI e; unsafe { e = Map.MapType <BTENTRYANSI>(p.rgentries, LayoutsA.BTPAGEEntryBytes, i * p.cbEnt, p.cbEnt); } var inter = new TreeIntermediate { Key = e.btkey }; parent.Children.Add(inter); if (deferReadingNodes) { inter.fileOffset = e.BREF.ib; } else { // Read child nodes in tree ReadBTPageANSI(fs, e.BREF.ib, inter); inter.fileOffset = null; } } else { if (p.pageTrailer.ptype == Eptype.ptypeNBT) { unsafe { var e = Map.MapType <NBTENTRYANSI>(p.rgentries, LayoutsA.BTPAGEEntryBytes, i * p.cbEnt, p.cbEnt); var nb = new Node { Key = e.nid.dwValue, Type = e.nid.nidType, DataBid = e.bidData, SubDataBid = e.bidSub, Parent = e.nidParent }; parent.Children.Add(nb); } } else if (p.pageTrailer.ptype == Eptype.ptypeBBT) { unsafe { var e = Map.MapType <BBTENTRYANSI>(p.rgentries, LayoutsA.BTPAGEEntryBytes, i * p.cbEnt, p.cbEnt); parent.Children.Add(new DataRef { Key = e.BREF.bid, Offset = e.BREF.ib, Length = e.cb }); } } else { throw new XstException("Unexpected page entry type"); } } } }