Skip to content
This repository was archived by the owner on Jun 24, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.archivepatcher.shared.DeflateUncompressor;
import com.google.archivepatcher.shared.RandomAccessFileInputStream;
import com.google.archivepatcher.shared.Uncompressor;
import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -119,12 +120,16 @@ public List<EntryExplanation> explainPatch(
}

Uncompressor uncompressor = new DeflateUncompressor();
PreDiffExecutor executor =
new PreDiffExecutor.Builder()
.readingOriginalFiles(oldFile, newFile)
.addPreDiffPlanEntryModifiers(Arrays.asList(preDiffPlanEntryModifiers))
.build();
PreDiffPlan plan = executor.prepareForDiffing();
PreDiffPlan plan;
try (ByteSource oldBlob = ByteSource.fromFile(oldFile);
ByteSource newBlob = ByteSource.fromFile(newFile)) {
PreDiffExecutor executor =
new PreDiffExecutor.Builder()
.readingOriginalFiles(oldBlob, newBlob)
.addPreDiffPlanEntryModifiers(Arrays.asList(preDiffPlanEntryModifiers))
.build();
plan = executor.prepareForDiffing();
}
try (TempFileHolder oldTemp = new TempFileHolder();
TempFileHolder newTemp = new TempFileHolder();
TempFileHolder deltaTemp = new TempFileHolder()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import com.google.archivepatcher.shared.Compressor;
import com.google.archivepatcher.shared.UnitTestZipArchive;
import com.google.archivepatcher.shared.UnitTestZipEntry;
import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -96,7 +96,7 @@ public void compress(InputStream uncompressedIn, OutputStream compressedOut)
* A "delta generator" that always outputs the same exact string regardless of the inputs and
* asserts that the input is exactly as expected.
*/
private static class FakeDeltaGenerator implements DeltaGenerator {
private static class FakeDeltaGenerator extends DeltaGenerator {
static final String OUTPUT = "fakedeltagenerator output";
private final byte[] expectedOld;
private final byte[] expectedNew;
Expand All @@ -107,17 +107,18 @@ public FakeDeltaGenerator(byte[] expectedOld, byte[] expectedNew) {
}

@Override
public void generateDelta(File oldBlob, File newBlob, OutputStream deltaOut)
public void generateDelta(ByteSource oldBlob, ByteSource newBlob, OutputStream deltaOut)
throws IOException {
assertFileEquals(oldBlob, expectedOld);
assertFileEquals(newBlob, expectedNew);
assertByteSourceEquals(oldBlob, expectedOld);
assertByteSourceEquals(newBlob, expectedNew);
deltaOut.write(OUTPUT.getBytes("US-ASCII"));
}

private final void assertFileEquals(File file, byte[] expected) throws IOException {
byte[] actual = new byte[(int) file.length()];
try (FileInputStream fileIn = new FileInputStream(file);
DataInputStream dataIn = new DataInputStream(fileIn)) {
private final void assertByteSourceEquals(ByteSource byteSource, byte[] expected)
throws IOException {
byte[] actual = new byte[(int) byteSource.length()];
try (InputStream in = byteSource.openStream();
DataInputStream dataIn = new DataInputStream(in)) {
dataIn.readFully(actual);
}
assertThat(actual).isEqualTo(expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@

package com.google.archivepatcher.generator;

import static com.google.archivepatcher.shared.bytesource.ByteStreams.readFully;

import com.google.archivepatcher.shared.DefaultDeflateCompatibilityWindow;
import com.google.archivepatcher.shared.JreDeflateParameters;
import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -78,7 +80,26 @@ public DivinationResult(
/**
* Load the specified archive and attempt to divine deflate parameters for all entries within.
*
* @param archiveFile the archive file to work on
* @param archiveBlob the archive blob to work on
* @return a list of results for each entry in the archive, in file order (not central directory
* order). There is exactly one result per entry, regardless of whether or not that entry is
* compressed. Callers can filter results by checking {@link
* MinimalZipEntry#getCompressionMethod()} to see if the result is or is not compressed, and
* by checking whether a non-null {@link JreDeflateParameters} was obtained.
* @throws IOException if unable to read or parse the file
* @see DivinationResult
*/
public static List<DivinationResult> divineDeflateParameters(File archiveBlob)
throws IOException {
try (ByteSource archiveData = ByteSource.fromFile(archiveBlob)) {
return divineDeflateParameters(archiveData);
}
}

/**
* Load the specified archive and attempt to divine deflate parameters for all entries within.
*
* @param archiveBlob the archive blob to work on
* @return a list of results for each entry in the archive, in file order (not central directory
* order). There is exactly one result per entry, regardless of whether or not that entry is
* compressed. Callers can filter results by checking {@link
Expand All @@ -87,10 +108,9 @@ public DivinationResult(
* @throws IOException if unable to read or parse the file
* @see DivinationResult
*/
public static List<DivinationResult> divineDeflateParameters(File archiveFile)
public static List<DivinationResult> divineDeflateParameters(ByteSource archiveBlob)
throws IOException {
RandomAccessFile randomAccessArchiveFile = new RandomAccessFile(archiveFile, "r");
List<MinimalZipEntry> zipEntries = MinimalZipArchive.listEntries(archiveFile);
List<MinimalZipEntry> zipEntries = MinimalZipArchive.listEntries(archiveBlob);
List<DivinationResult> results = new ArrayList<>(zipEntries.size());
for (MinimalZipEntry minimalZipEntry : zipEntries) {
JreDeflateParameters divinedParameters = null;
Expand All @@ -99,47 +119,45 @@ public static List<DivinationResult> divineDeflateParameters(File archiveFile)
if (minimalZipEntry.getCompressedSize() < (100 * 1024)) {
try {
byte[] compressedBytes = new byte[(int) minimalZipEntry.getCompressedSize()];
randomAccessArchiveFile.seek(minimalZipEntry.getFileOffsetOfCompressedData());
randomAccessArchiveFile.readFully(compressedBytes);
divinedParameters =
divineDeflateParameters(new ByteArrayInputStreamFactory(compressedBytes));
try (InputStream in =
archiveBlob
.slice(minimalZipEntry.getFileOffsetOfCompressedData(), compressedBytes.length)
.openStream()) {
readFully(in, compressedBytes);
}
divinedParameters = divineDeflateParametersForEntry(ByteSource.wrap(compressedBytes));
} catch (Exception ignore) {
divinedParameters = null;
}
} else {
divinedParameters =
divineDeflateParameters(
new RandomAccessFileInputStreamFactory(
archiveFile,
minimalZipEntry.getFileOffsetOfCompressedData(),
minimalZipEntry.getCompressedSize()));
try (ByteSource slice =
archiveBlob.slice(
minimalZipEntry.getFileOffsetOfCompressedData(),
minimalZipEntry.getCompressedSize())) {
divinedParameters = divineDeflateParametersForEntry(slice);
}
}
}
results.add(new DivinationResult(minimalZipEntry, divinedParameters));
}
try {
randomAccessArchiveFile.close();
} catch (Exception ignore) {
// Ignore
}
return results;
}

/**
* Determines the original {@link JreDeflateParameters} that were used to compress a given piece
* of deflated delivery.
*
* @param compressedDataInputStreamFactory a {@link MultiViewInputStreamFactory} that can provide
* multiple independent {@link InputStream} instances for the compressed delivery.
* @param entry a {@link MultiViewInputStreamFactory} that can provide multiple independent {@link
* InputStream} instances for the compressed delivery.
* @return the parameters that can be used to replicate the compressed delivery in the {@link
* DefaultDeflateCompatibilityWindow}, if any; otherwise <code>null</code>. Note that <code>
* null</code> is also returned in the case of <em>corrupt</em> zip delivery since, by
* definition, it cannot be replicated via any combination of normal deflate parameters.
* @throws IOException if there is a problem reading the delivery, i.e. if the file contents are
* changed while reading
*/
public static JreDeflateParameters divineDeflateParameters(
MultiViewInputStreamFactory compressedDataInputStreamFactory) throws IOException {
public static JreDeflateParameters divineDeflateParametersForEntry(ByteSource entry)
throws IOException {
byte[] copyBuffer = new byte[32 * 1024];
// Iterate over all relevant combinations of nowrap, strategy and level.
for (boolean nowrap : new boolean[] {true, false}) {
Expand All @@ -154,7 +172,7 @@ public static JreDeflateParameters divineDeflateParameters(
inflater.reset();
deflater.reset();
try {
if (matches(inflater, deflater, compressedDataInputStreamFactory, copyBuffer)) {
if (matches(inflater, deflater, entry, copyBuffer)) {
end(inflater, deflater);
return JreDeflateParameters.of(level, strategy, nowrap);
}
Expand Down Expand Up @@ -214,25 +232,21 @@ private static void end(Inflater inflater, Deflater deflater) {
*
* @param inflater the inflater for uncompressing the stream
* @param deflater the deflater for recompressing the output of the inflater
* @param compressedData {@link ByteSource} containing the compressed data.
* @param copyBuffer buffer to use for copying bytes between the inflater and the deflater
* @return true if the specified deflater reproduces the bytes in compressedDataIn, otherwise
* false
* @throws IOException if anything goes wrong; in particular, {@link ZipException} is thrown if
* there is a problem parsing compressedDataIn
*/
private static boolean matches(
Inflater inflater,
Deflater deflater,
MultiViewInputStreamFactory compressedDataInputStreamFactory,
byte[] copyBuffer)
Inflater inflater, Deflater deflater, ByteSource compressedData, byte[] copyBuffer)
throws IOException {

try (MatchingOutputStream matcher =
new MatchingOutputStream(
compressedDataInputStreamFactory.newStream(), copyBuffer.length);
new MatchingOutputStream(compressedData.openStream(), copyBuffer.length);
InflaterInputStream inflaterIn =
new InflaterInputStream(
compressedDataInputStreamFactory.newStream(), inflater, copyBuffer.length);
new InflaterInputStream(compressedData.openStream(), inflater, copyBuffer.length);
DeflaterOutputStream out = new DeflaterOutputStream(matcher, deflater, copyBuffer.length)) {
int numRead;
while ((numRead = inflaterIn.read(copyBuffer)) >= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

package com.google.archivepatcher.generator;

import java.io.File;
import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -65,7 +65,7 @@ public DeltaFriendlyOldBlobSizeLimiter(long maxSizeBytes) {

@Override
public List<PreDiffPlanEntry> getModifiedPreDiffPlanEntries(
File oldFile, File newFile, List<PreDiffPlanEntry> originalEntries) {
ByteSource oldFile, ByteSource newFile, List<PreDiffPlanEntry> originalEntries) {

List<PreDiffPlanEntry> sorted = sortPreDiffPlanEntries(originalEntries);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@

package com.google.archivepatcher.generator;

import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
* An interface to be implemented by delta generators.
*/
public interface DeltaGenerator {
/** An interface to be implemented by delta generators. */
public abstract class DeltaGenerator {
/**
* Generates a delta in deltaOut that can be applied to oldBlob to produce newBlob.
*
Expand All @@ -32,6 +31,24 @@ public interface DeltaGenerator {
* delta output stream
* @throws InterruptedException if any thread has interrupted the current thread
*/
void generateDelta(File oldBlob, File newBlob, OutputStream deltaOut)
public void generateDelta(File oldBlob, File newBlob, OutputStream deltaOut)
throws IOException, InterruptedException {
try (ByteSource oldByteSource = ByteSource.fromFile(oldBlob);
ByteSource newByteSource = ByteSource.fromFile(newBlob)) {
generateDelta(oldByteSource, newByteSource, deltaOut);
}
}

/**
* Generates a delta in deltaOut that can be applied to oldBlob to produce newBlob.
*
* @param oldBlob the old blob
* @param newBlob the new blob
* @param deltaOut the stream to write the delta to
* @throws IOException in the event of an I/O error reading the input files or writing to the
* delta output stream
* @throws InterruptedException if any thread has interrupted the current thread
*/
public abstract void generateDelta(ByteSource oldBlob, ByteSource newBlob, OutputStream deltaOut)
throws IOException, InterruptedException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.archivepatcher.generator.bsdiff.BsDiffDeltaGenerator;
import com.google.archivepatcher.shared.PatchConstants.DeltaFormat;
import com.google.archivepatcher.shared.bytesource.ByteSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
Expand All @@ -30,7 +31,7 @@
import java.util.Set;

/** Generates file-by-file patches. */
public class FileByFileDeltaGenerator implements DeltaGenerator {
public class FileByFileDeltaGenerator extends DeltaGenerator {

/** Modifiers for planning and patch generation. */
private final List<PreDiffPlanEntryModifier> preDiffPlanEntryModifiers;
Expand Down Expand Up @@ -84,14 +85,14 @@ public FileByFileDeltaGenerator(
* OutputStream} or in a post-processing step, prior to transmitting the patch to the patch
* applier.
*
* @param oldFile the original old file to read (will not be modified)
* @param newFile the original new file to read (will not be modified)
* @param oldBlob the original old file to read (will not be modified)
* @param newBlob the original new file to read (will not be modified)
* @param patchOut the stream to write the patch to
* @throws IOException if unable to complete the operation due to an I/O error
* @throws InterruptedException if any thread has interrupted the current thread
*/
@Override
public void generateDelta(File oldFile, File newFile, OutputStream patchOut)
public void generateDelta(ByteSource oldBlob, ByteSource newBlob, OutputStream patchOut)
throws IOException, InterruptedException {
try (TempFileHolder deltaFriendlyOldFile = new TempFileHolder();
TempFileHolder deltaFriendlyNewFile = new TempFileHolder();
Expand All @@ -100,7 +101,7 @@ public void generateDelta(File oldFile, File newFile, OutputStream patchOut)
BufferedOutputStream bufferedDeltaOut = new BufferedOutputStream(deltaFileOut)) {
PreDiffPlan preDiffPlan =
generatePreDiffPlan(
oldFile, newFile, deltaFriendlyOldFile, deltaFriendlyNewFile, supportedDeltaFormats);
oldBlob, newBlob, deltaFriendlyOldFile, deltaFriendlyNewFile, supportedDeltaFormats);
DeltaGenerator deltaGenerator = getDeltaGenerator();
deltaGenerator.generateDelta(
deltaFriendlyOldFile.file, deltaFriendlyNewFile.file, bufferedDeltaOut);
Expand All @@ -124,15 +125,17 @@ public void generateDelta(File oldFile, File newFile, OutputStream patchOut)
*/
public PreDiffPlan generatePreDiffPlan(File oldFile, File newFile) throws IOException {
try (TempFileHolder deltaFriendlyOldFile = new TempFileHolder();
TempFileHolder deltaFriendlyNewFile = new TempFileHolder()) {
TempFileHolder deltaFriendlyNewFile = new TempFileHolder();
ByteSource oldBlob = ByteSource.fromFile(oldFile);
ByteSource newBlob = ByteSource.fromFile(newFile)) {
return generatePreDiffPlan(
oldFile, newFile, deltaFriendlyOldFile, deltaFriendlyNewFile, supportedDeltaFormats);
oldBlob, newBlob, deltaFriendlyOldFile, deltaFriendlyNewFile, supportedDeltaFormats);
}
}

private PreDiffPlan generatePreDiffPlan(
File oldFile,
File newFile,
ByteSource oldFile,
ByteSource newFile,
TempFileHolder deltaFriendlyOldFile,
TempFileHolder deltaFriendlyNewFile,
Set<DeltaFormat> supportedDeltaFormats)
Expand Down
Loading