private VCDiffResult DecodeCore() { using ByteBuffer instructionBuffer = new ByteBuffer(window.InstructionsAndSizesData); using ByteBuffer addressBuffer = new ByteBuffer(window.AddressesForCopyData); using ByteBuffer addRunBuffer = new ByteBuffer(window.AddRunData); InstructionDecoder instrDecoder = new InstructionDecoder(instructionBuffer, customTable); VCDiffResult result = VCDiffResult.SUCCESS; while (this.TotalBytesDecoded < window.TargetWindowLength) { VCDiffInstructionType instruction = instrDecoder.Next(out int decodedSize, out byte mode); switch (instruction) { case VCDiffInstructionType.EOD: targetData.SetLength(0); return(VCDiffResult.EOD); case VCDiffInstructionType.ERROR: targetData.SetLength(0); return(VCDiffResult.ERROR); } switch (instruction) { case VCDiffInstructionType.ADD: result = DecodeAdd(decodedSize, addRunBuffer); break; case VCDiffInstructionType.RUN: result = DecodeRun(decodedSize, addRunBuffer); break; case VCDiffInstructionType.COPY: result = DecodeCopy(decodedSize, mode, addressBuffer); break; default: targetData.SetLength(0); return(VCDiffResult.ERROR); } } if (window.ChecksumFormat == ChecksumFormat.SDCH) { uint adler = Checksum.ComputeGoogleAdler32(targetData.GetBuffer().AsMemory(0, (int)targetData.Length)); if (adler != window.Checksum) { result = VCDiffResult.ERROR; } } else if (window.ChecksumFormat == ChecksumFormat.Xdelta3) { uint adler = Checksum.ComputeXdelta3Adler32(targetData.GetBuffer().AsMemory(0, (int)targetData.Length)); if (adler != window.Checksum) { result = VCDiffResult.ERROR; } } return(result); }
/// <summary> /// Encodes the data using the settings from initialization /// </summary> /// <param name="newData">the target data</param> /// <param name="outputStream">the out stream</param> public unsafe void EncodeChunk(ByteBuffer newData, Stream outputStream) { newData.Position = 0; ReadOnlyMemory <byte> checksumBytes = newData.ReadBytes((int)newData.Length); uint checksum = this.checksumFormat switch { ChecksumFormat.SDCH => Checksum.ComputeGoogleAdler32(checksumBytes), ChecksumFormat.Xdelta3 => Checksum.ComputeXdelta3Adler32(checksumBytes), ChecksumFormat.None => 0, _ => 0 }; windowEncoder = new WindowEncoder(oldData.Length, checksum, this.checksumFormat, this.interleaved); oldData.Position = 0; newData.Position = 0; long nextEncode = newData.Position; long targetEnd = newData.Length; long startOfLastBlock = targetEnd - this.dictionary.blockSize; long candidatePos = nextEncode; ulong hash; //create the first hash hash = hasher.Hash(newData.DangerousGetBytePointerAtCurrentPositionAndIncreaseOffsetAfter(0), this.dictionary.blockSize); byte *newDataPtr = newData.DangerousGetBytePointer(); while (true) { //if less than block size exit and then write as an ADD if (newData.Length - nextEncode < this.dictionary.blockSize) { break; } //try and encode the copy and add instructions that best match var bytesEncoded = EncodeCopyForBestMatch(hash, candidatePos, nextEncode, targetEnd, newDataPtr, newData); if (bytesEncoded > 0) { nextEncode += bytesEncoded; candidatePos = nextEncode; if (candidatePos > startOfLastBlock) { break; } newData.Position = candidatePos; //cannot use rolling hash since we skipped so many hash = hasher.Hash(newData.DangerousGetBytePointerAtCurrentPositionAndIncreaseOffsetAfter(this.dictionary.blockSize), this.dictionary.blockSize); } else { if (candidatePos + 1 > startOfLastBlock) { break; } //update hash requires the first byte of the last hash as well as the byte that is first byte pos + blockSize //in order to properly calculate the rolling hash newData.Position = candidatePos; byte peek0 = newData.ReadByte(); newData.Position = candidatePos + this.dictionary.blockSize; byte peek1 = newData.ReadByte(); hash = hasher.UpdateHash(hash, peek0, peek1); candidatePos++; } } //Add the rest of the data that was not encoded if (nextEncode < newData.Length) { int len = (int)(newData.Length - nextEncode); newData.Position = nextEncode; windowEncoder.Add(newData.ReadBytes(len).Span); } //output the final window windowEncoder.Output(outputStream); }