/// <summary> /// Serializes all data. /// </summary> /// <param name="child">The child node to serialize.</param> /// <param name="ms">The binary writer backed by memory.</param> /// <param name="writeStat">If the node stat should be serialized along with node data</param> private static void SerializeAllData(Node child, BinaryWriter ms, bool writeStat) { ms.Write(child.Name); if (child.Data == null) { ms.Write(-1); } else { ms.Write(child.Data.Length); ms.Write(child.Data); } ms.Write(writeStat); if (writeStat) { child.NodeStat.Write(ms); } CompleteNode cn = child as CompleteNode; if (cn != null) { foreach (IPersistedData n in cn.ChildrenNodes) { SerializeAllData(n.Node, ms, writeStat); } } ms.Write(string.Empty); }
/// <summary> /// Serializes all data in a depth-first manner sorted by node name, supporting continuations. /// </summary> /// <param name="child">Node to serialize all the data under.</param> /// <param name="ms">Binary writer to serialize the data to.</param> /// <param name="writeStat">Whether to write stats for each node or not.</param> /// <param name="startingPath">Continuation path to resume from.</param> /// <param name="continuationPathBuilder">Continuation path result if we hit max number of nodes limit.</param> /// <param name="maxNodes">Maximum number of new nodes to serialize.</param> /// <returns>True if enumeration was fully completed, false if the max nodes limit was hit.</returns> private static bool SerializeAllDataSorted(Node child, BinaryWriter ms, bool writeStat, Queue <string> startingPath, Stack <string> continuationPathBuilder, ref int maxNodes) { ms.Write(child.Name); if (startingPath != null && startingPath.Count > 0) { var nextNodeName = startingPath.Dequeue(); if (child.Name != nextNodeName) { throw new ArgumentException($"Invalid starting path specified. Current node is {child.Name}, but starting name is {nextNodeName}", nameof(startingPath)); } // not including data for this node as it was part of previous continuation ms.Write(-1); // not including stat for this node as it was part of previous continuation ms.Write(false); } else { maxNodes--; if (child.Data == null) { ms.Write(-1); } else { ms.Write(child.Data.Length); ms.Write(child.Data); } ms.Write(writeStat); if (writeStat) { child.NodeStat.Write(ms); } } if (maxNodes <= 0) { continuationPathBuilder.Push(child.Name == "/" ? "/" : string.Concat("/", child.Name)); ms.Write(string.Empty); return(false); } CompleteNode cn = child as CompleteNode; if (cn != null) { string nextNodeName = string.Empty; if (startingPath != null && startingPath.Count > 0) { nextNodeName = startingPath.Peek(); IPersistedData n; if (cn.ChildrenMapping.TryGetValue(nextNodeName, out n)) { if (!SerializeAllDataSorted(n.Node, ms, writeStat, startingPath, continuationPathBuilder, ref maxNodes)) { continuationPathBuilder.Push(string.Concat("/", child.Name)); ms.Write(string.Empty); return(false); } } else { // this subtree was deleted so we can forget about it and continue with next node in order startingPath.Clear(); } } var sortedChildren = cn.RetrieveChildren($">:{maxNodes}:{nextNodeName}"); foreach (var childNodeName in sortedChildren) { IPersistedData n = cn.ChildrenMapping[childNodeName]; if (!SerializeAllDataSorted(n.Node, ms, writeStat, startingPath, continuationPathBuilder, ref maxNodes)) { continuationPathBuilder.Push(string.Concat("/", child.Name)); ms.Write(string.Empty); return(false); } } } ms.Write(string.Empty); return(true); }