/// <summary> /// Calculates all expanded text. Instead of performing the expansion, a /// tree of expansion instructions is built. Then we calculate the actual /// file length by calculating the effect of all the expansions /// </summary> public long CalculateBetaProtocolFileLength(string input) { string cleanedInput; FileCompressionInstruction rootInstruction = new FileCompressionInstruction(1, 1); ParseFileInstructions(input, rootInstruction.UnpackedInstructions, out cleanedInput); rootInstruction.SourceText = cleanedInput; rootInstruction.CharCount = cleanedInput.Length; ExpandInstructions(rootInstruction); return(CalcExpandedInstructionLength(rootInstruction)); }
/// <summary> /// Recursively expands any instructions left in the sourcetext of the supplied instruction. /// </summary> private void ExpandInstructions(FileCompressionInstruction rootInstruction) { foreach (FileCompressionInstruction instruction in rootInstruction.UnpackedInstructions) { string cleanedSource; ParseFileInstructions(instruction.SourceText, instruction.UnpackedInstructions, out cleanedSource); // Update the source text, child instructions are now kept. note that repeats stay the same instruction.SourceText = cleanedSource; // Character count has to be kept up to date instruction.CharCount = cleanedSource.Length; ExpandInstructions(instruction); } }
/// <summary> /// Recursively calculates the instruction length /// </summary> private long CalcExpandedInstructionLength(FileCompressionInstruction rootInstruction) { // Get the length of the clean text in this instruction long result = rootInstruction.CharCount; // recurse down the hierarchy, adding up all expanded values as we go foreach (var childInstruction in rootInstruction.UnpackedInstructions) { result += CalcExpandedInstructionLength(childInstruction); } // Now take the hierarchy length, and multiply it with this item's count of repeats return(result * rootInstruction.Repeats); }
/// <summary> /// Take a string that may contain expanding instructions /// Example 1: "(1x3)a" becomes "", and an instruction is returned that indicates that A /// should be multipled 3 times /// Example 2: "pp(5x2)(1x3)f" becomes "pp", and an instruction is returned that indicates /// that "(1x3)f should be repeated 2 times /// /// Note that we don't attempt to unpack all the files, instead we reprocess our recursive data structure /// in a controlling routine to unpack the child instructions /// </summary> private void ParseFileInstructions(string input, List <FileCompressionInstruction> instructions, out string cleanedInput) { bool inInstruction = false; StringBuilder output = new StringBuilder(); StringBuilder instruction = null; int currentPosition = -1; while (currentPosition < input.Length - 1) { currentPosition++; char c = input[currentPosition]; if (c == '(') { if (inInstruction) { throw new ApplicationException("in an instruction and got another ( char. WTF?"); } instruction = new StringBuilder(); inInstruction = true; continue; } if (c == ')') { if (!inInstruction) { throw new ApplicationException("Not in an instruction and got a ) char. WTF?"); } string instructionText = instruction.ToString(); FileCompressionInstruction parsedInstruction = new FileCompressionInstruction(instructionText); parsedInstruction.SourceText = input.Substring(currentPosition + 1, parsedInstruction.CharCount); currentPosition += parsedInstruction.CharCount; instructions.Add(parsedInstruction); inInstruction = false; continue; } if (inInstruction) { instruction.Append(c); } else { output.Append(c); } } cleanedInput = output.ToString(); }