/// <summary> /// Reads and decodes a window, returning whether or not there was /// any more data to read. /// </summary> /// <returns> /// Whether or not the delta stream had reached the end of its data. /// </returns> private bool DecodeWindow() { int windowIndicator = delta.ReadByte(); // Have we finished? if (windowIndicator == -1) { return(false); } // The stream to load source data from for this window, if any Stream sourceStream; // Where to reposition the source stream to after reading from it, if anywhere int sourceStreamPostReadSeek = -1; // xdelta3 uses an undocumented extra bit which indicates that there are an extra // 4 bytes at the end of the encoding for the window bool hasAdler32Checksum = ((windowIndicator & 4) == 4); // Get rid of the checksum bit for the rest windowIndicator &= 0xfb; // Work out what the source data is, and detect invalid window indicators switch (windowIndicator & 3) { // No source data used in this window case 0: sourceStream = null; break; // Source data comes from the original stream case 1: if (original == null) { throw new VcdiffFormatException ("Source stream requested by delta but not provided by caller."); } sourceStream = original; break; case 2: sourceStream = output; sourceStreamPostReadSeek = (int)output.Position; break; case 3: throw new VcdiffFormatException ("Invalid window indicator - bits 0 and 1 both set."); default: throw new VcdiffFormatException("Invalid window indicator - bits 3-7 not all zero."); } // Read the source data, if any byte[] sourceData = null; int sourceLength = 0; if (sourceStream != null) { sourceLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int sourcePosition = IOHelper.ReadBigEndian7BitEncodedInt(delta); sourceStream.Position = sourcePosition; sourceData = IOHelper.CheckedReadBytes(sourceStream, sourceLength); // Reposition the source stream if appropriate if (sourceStreamPostReadSeek != -1) { sourceStream.Position = sourceStreamPostReadSeek; } } // Read how long the delta encoding is - then ignore it IOHelper.ReadBigEndian7BitEncodedInt(delta); // Read how long the target window is int targetLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); byte[] targetData = new byte[targetLength]; MemoryStream targetDataStream = new MemoryStream(targetData, true); // Read the indicator and the lengths of the different data sections byte deltaIndicator = IOHelper.CheckedReadByte(delta); if (deltaIndicator != 0) { throw new VcdiffFormatException("VcdiffDecoder is unable to handle compressed delta sections."); } int addRunDataLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int instructionsLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int addressesLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); // If we've been given a checksum, we have to read it and we might as well // use it to check the data. int checksumInFile = 0; if (hasAdler32Checksum) { byte[] checksumBytes = IOHelper.CheckedReadBytes(delta, 4); checksumInFile = (checksumBytes[0] << 24) | (checksumBytes[1] << 16) | (checksumBytes[2] << 8) | checksumBytes[3]; } // Read all the data for this window byte[] addRunData = IOHelper.CheckedReadBytes(delta, addRunDataLength); byte[] instructions = IOHelper.CheckedReadBytes(delta, instructionsLength); byte[] addresses = IOHelper.CheckedReadBytes(delta, addressesLength); int addRunDataIndex = 0; MemoryStream instructionStream = new MemoryStream(instructions, false); _cache.Reset(addresses); while (true) { int instructionIndex = instructionStream.ReadByte(); if (instructionIndex == -1) { break; } for (int i = 0; i < 2; i++) { Instruction instruction = _codeTable[instructionIndex, i]; int size = instruction.Size; if (size == 0 && instruction.Type != InstructionType.NoOp) { size = IOHelper.ReadBigEndian7BitEncodedInt(instructionStream); } switch (instruction.Type) { case InstructionType.NoOp: break; case InstructionType.Add: targetDataStream.Write(addRunData, addRunDataIndex, size); addRunDataIndex += size; break; case InstructionType.Copy: int addr = _cache.DecodeAddress((int)targetDataStream.Position + sourceLength, instruction.Mode); if (sourceData != null && addr < sourceData.Length) { targetDataStream.Write(sourceData, addr, size); } else // Data is in target data { // Get rid of the offset addr -= sourceLength; // Can we just ignore overlap issues? if (addr + size < targetDataStream.Position) { targetDataStream.Write(targetData, addr, size); } else { for (int j = 0; j < size; j++) { targetDataStream.WriteByte(targetData[addr++]); } } } break; case InstructionType.Run: byte data = addRunData[addRunDataIndex++]; for (int j = 0; j < size; j++) { targetDataStream.WriteByte(data); } break; default: throw new VcdiffFormatException("Invalid instruction type found."); } } } output.Write(targetData, 0, targetLength); if (hasAdler32Checksum) { int actualChecksum = Adler32.ComputeChecksum(1, targetData); if (actualChecksum != checksumInFile) { throw new VcdiffFormatException("Invalid checksum after decoding window"); } } return(true); }