All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nothome.delta.GDiffPatcher Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2001 Torgeir Veimo
 * Copyright (c) 2006 Heiko Klein
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 */
package com.nothome.delta;

import static com.nothome.delta.GDiffWriter.COPY_INT_INT;
import static com.nothome.delta.GDiffWriter.COPY_INT_UBYTE;
import static com.nothome.delta.GDiffWriter.COPY_INT_USHORT;
import static com.nothome.delta.GDiffWriter.COPY_LONG_INT;
import static com.nothome.delta.GDiffWriter.COPY_USHORT_INT;
import static com.nothome.delta.GDiffWriter.COPY_USHORT_UBYTE;
import static com.nothome.delta.GDiffWriter.COPY_USHORT_USHORT;
import static com.nothome.delta.GDiffWriter.DATA_INT;
import static com.nothome.delta.GDiffWriter.DATA_MAX;
import static com.nothome.delta.GDiffWriter.DATA_USHORT;
import static com.nothome.delta.GDiffWriter.EOF;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;

/**
 * This class patches an input file with a GDIFF patch file.
 *
 * The patch file follows the GDIFF file specification available at
 * NOTE-gdiff-19970901.
 */
public class GDiffPatcher {
  /** The buf. */
  private ByteBuffer buf = ByteBuffer.allocate(1024);
  /** The buf2. */
  private byte buf2[] = buf.array();

  /**
   * Constructs a new GDiffPatcher.
   */
  public GDiffPatcher() {}

  /**
   * Patches to an output file.
   *
   * @param sourceFile the source file
   * @param patchFile the patch file
   * @param outputFile the output file
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public void patch(File sourceFile, File patchFile, File outputFile) throws IOException {
    RandomAccessFileSeekableSource source = new RandomAccessFileSeekableSource(new RandomAccessFile(sourceFile, "r"));
    InputStream patch = new FileInputStream(patchFile);
    OutputStream output = new FileOutputStream(outputFile);
    try {
      patch(source, patch, output);
    } catch (IOException e) {
      throw e;
    } finally {
      source.close();
      patch.close();
      output.close();
    }
  }

  /**
   * Patches to an output stream.
   *
   * @param source the source
   * @param patch the patch
   * @param output the output
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public void patch(byte[] source, InputStream patch, OutputStream output) throws IOException {
    patch(new ByteBufferSeekableSource(source), patch, output);
  }

  /**
   * Patches in memory, returning the patch result.
   *
   * @param source the source
   * @param patch the patch
   * @return the byte[]
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public byte[] patch(byte[] source, byte[] patch) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    patch(source, new ByteArrayInputStream(patch), os);
    return os.toByteArray();
  }

  /**
   * Patches to an output stream.
   *
   * @param source the source
   * @param patch the patch
   * @param out the out
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public void patch(SeekableSource source, InputStream patch, OutputStream out) throws IOException {
    DataOutputStream outOS = new DataOutputStream(out);
    DataInputStream patchIS = new DataInputStream(patch);
    // the magic string is 'd1 ff d1 ff' + the version number
    if (patchIS.readUnsignedByte() != 0xd1 || patchIS.readUnsignedByte() != 0xff || patchIS.readUnsignedByte() != 0xd1 || patchIS.readUnsignedByte() != 0xff || patchIS.readUnsignedByte() != 0x04) {
      throw new PatchException("magic string not found, aborting!");
    }
    while (true) {
      int command = patchIS.readUnsignedByte();
      if (command == EOF)
        break;
      int length;
      int offset;
      if (command <= DATA_MAX) {
        append(command, patchIS, outOS);
        continue;
      }
      switch (command) {
        case DATA_USHORT: // ushort, n bytes following; append
          length = patchIS.readUnsignedShort();
          append(length, patchIS, outOS);
          break;
        case DATA_INT: // int, n bytes following; append
          length = patchIS.readInt();
          append(length, patchIS, outOS);
          break;
        case COPY_USHORT_UBYTE:
          offset = patchIS.readUnsignedShort();
          length = patchIS.readUnsignedByte();
          copy(offset, length, source, outOS);
          break;
        case COPY_USHORT_USHORT:
          offset = patchIS.readUnsignedShort();
          length = patchIS.readUnsignedShort();
          copy(offset, length, source, outOS);
          break;
        case COPY_USHORT_INT:
          offset = patchIS.readUnsignedShort();
          length = patchIS.readInt();
          copy(offset, length, source, outOS);
          break;
        case COPY_INT_UBYTE:
          offset = patchIS.readInt();
          length = patchIS.readUnsignedByte();
          copy(offset, length, source, outOS);
          break;
        case COPY_INT_USHORT:
          offset = patchIS.readInt();
          length = patchIS.readUnsignedShort();
          copy(offset, length, source, outOS);
          break;
        case COPY_INT_INT:
          offset = patchIS.readInt();
          length = patchIS.readInt();
          copy(offset, length, source, outOS);
          break;
        case COPY_LONG_INT:
          long loffset = patchIS.readLong();
          length = patchIS.readInt();
          copy(loffset, length, source, outOS);
          break;
        default:
          throw new IllegalStateException("command " + command);
      }
    }
    outOS.flush();
  }

  /**
   * Copy.
   *
   * @param offset the offset
   * @param length the length
   * @param source the source
   * @param output the output
   * @throws IOException Signals that an I/O exception has occurred.
   */
  private void copy(long offset, int length, SeekableSource source, OutputStream output) throws IOException {
    source.seek(offset);
    while (length > 0) {
      int len = Math.min(buf.capacity(), length);
      buf.clear().limit(len);
      int res = source.read(buf);
      if (res == -1)
        throw new EOFException("in copy " + offset + " " + length);
      output.write(buf.array(), 0, res);
      length -= res;
    }
  }

  /**
   * Append.
   *
   * @param length the length
   * @param patch the patch
   * @param output the output
   * @throws IOException Signals that an I/O exception has occurred.
   */
  private void append(int length, InputStream patch, OutputStream output) throws IOException {
    while (length > 0) {
      int len = Math.min(buf2.length, length);
      int res = patch.read(buf2, 0, len);
      if (res == -1)
        throw new EOFException("cannot read " + length);
      output.write(buf2, 0, res);
      length -= res;
    }
  }

  /**
   * Simple command line tool to patch a file.
   *
   * @param argv the arguments
   */
  public static void main(String argv[]) {
    if (argv.length != 3) {
      System.err.println("usage GDiffPatch source patch output");
      System.err.println("aborting..");
      return;
    }
    try {
      File sourceFile = new File(argv[0]);
      File patchFile = new File(argv[1]);
      File outputFile = new File(argv[2]);
      if (sourceFile.length() > Integer.MAX_VALUE || patchFile.length() > Integer.MAX_VALUE) {
        System.err.println("source or patch is too large, max length is " + Integer.MAX_VALUE);
        System.err.println("aborting..");
        return;
      }
      GDiffPatcher patcher = new GDiffPatcher();
      patcher.patch(sourceFile, patchFile, outputFile);
      System.out.println("finished patching file");
    } catch (Exception ioe) { //gls031504a
      System.err.println("error while patching: " + ioe);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy