private static SvnDiffInstruction ReadInstruction(BinaryReaderEOF reader)
        {
            if (reader.EOF)
            {
                return(null);
            }

            SvnDiffInstruction instruction = new SvnDiffInstruction();

            byte opCodeAndLength = reader.ReadByte();

            instruction.OpCode = (SvnDiffInstructionOpCode)((opCodeAndLength & 0xC0) >> 6);

            byte length = (byte)(opCodeAndLength & 0x3F);

            if (length == 0)
            {
                instruction.Length = ReadInt(reader);
            }
            else
            {
                instruction.Length = length;
            }

            if (instruction.OpCode == SvnDiffInstructionOpCode.CopyFromSource ||
                instruction.OpCode == SvnDiffInstructionOpCode.CopyFromTarget)
            {
                instruction.Offset = ReadInt(reader);
            }

            return(instruction);
        }
        public static SvnDiff[] ParseSvnDiff(Stream inputStream)
        {
            BinaryReaderEOF reader = new BinaryReaderEOF(inputStream);

            byte[] signature = reader.ReadBytes(3);
            byte   version   = reader.ReadByte();

            if (signature[0] != 'S' || signature[1] != 'V' || signature[2] != 'N')
            {
                throw new InvalidOperationException("The signature is invalid.");
            }
            if (version != 0)
            {
                throw new Exception("Unsupported SVN diff version");
            }

            List <SvnDiff> diffs = new List <SvnDiff>();

            while (!reader.EOF)
            {
                SvnDiff diff = new SvnDiff();

                diff.SourceViewOffset = ReadInt(reader);
                diff.SourceViewLength = ReadInt(reader);
                diff.TargetViewLength = ReadInt(reader);
                int instructionSectionLength = (int)ReadInt(reader);
                int dataSectionLength        = (int)ReadInt(reader);

                diff.InstructionSectionBytes = reader.ReadBytes(instructionSectionLength);
                diff.DataSectionBytes        = reader.ReadBytes(dataSectionLength);

                diffs.Add(diff);
            }
            return(diffs.ToArray());
        }
        public static byte[] ApplySvnDiff(SvnDiff svnDiff, byte[] source, int sourceDataStartIndex)
        {
            const int BUFFER_EXPAND_SIZE = 5000;

            byte[] buffer      = new byte[BUFFER_EXPAND_SIZE];
            int    targetIndex = 0;

            MemoryStream    instructionStream = new MemoryStream(svnDiff.InstructionSectionBytes);
            BinaryReaderEOF instructionReader = new BinaryReaderEOF(instructionStream);
            MemoryStream    dataStream        = new MemoryStream(svnDiff.DataSectionBytes);
            BinaryReader    dataReader        = new BinaryReader(dataStream);

            SvnDiffInstruction instruction = ReadInstruction(instructionReader);

            while (instruction != null)
            {
                if (targetIndex + (int)instruction.Length > buffer.Length)
                {
                    Array.Resize(ref buffer, buffer.Length + (int)instruction.Length + BUFFER_EXPAND_SIZE);
                }

                switch (instruction.OpCode)
                {
                case SvnDiffInstructionOpCode.CopyFromSource:
                    Array.Copy(source,
                               (int)instruction.Offset + sourceDataStartIndex,
                               buffer,
                               targetIndex,
                               (int)instruction.Length);
                    targetIndex += (int)instruction.Length;
                    break;

                case SvnDiffInstructionOpCode.CopyFromTarget:
                    // Cannot use Array.Copy because Offset + Length may be greater then starting targetIndex
                    for (int i = 0; i < (int)instruction.Length; i++)
                    {
                        buffer[targetIndex] = buffer[(int)instruction.Offset + i];
                        targetIndex++;
                    }
                    break;

                case SvnDiffInstructionOpCode.CopyFromNewData:
                    byte[] newData = dataReader.ReadBytes((int)instruction.Length);
                    Array.Copy(newData, 0, buffer, targetIndex, newData.Length);
                    targetIndex += newData.Length;
                    break;
                }

                instruction = ReadInstruction(instructionReader);
            }

            Array.Resize(ref buffer, targetIndex);
            return(buffer);
        }
        private static ulong ReadInt(BinaryReaderEOF reader, out int bytesRead)
        {
            ulong value = 0;

            bytesRead = 0;

            byte b = reader.ReadByte();

            bytesRead++;

            while ((b & 0x80) != 0)
            {
                value  |= (byte)(b & 0x7F);
                value <<= 7;

                b = reader.ReadByte();
                bytesRead++;
            }

            value |= (ulong)b;

            return(value);
        }
        private static ulong ReadInt(BinaryReaderEOF reader)
        {
            int bytesRead;

            return(ReadInt(reader, out bytesRead));
        }