BinaryDelta.java

  1. /*
  2.  * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  3.  * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.internal.storage.pack;

  12. import org.eclipse.jgit.internal.JGitText;
  13. import org.eclipse.jgit.util.QuotedString;
  14. import org.eclipse.jgit.util.RawParseUtils;

  15. /**
  16.  * Recreate a stream from a base stream and a GIT pack delta.
  17.  * <p>
  18.  * This entire class is heavily cribbed from <code>patch-delta.c</code> in the
  19.  * GIT project. The original delta patching code was written by Nicolas Pitre
  20.  * (&lt;nico@cam.org&gt;).
  21.  * </p>
  22.  */
  23. public class BinaryDelta {
  24.     /**
  25.      * Length of the base object in the delta stream.
  26.      *
  27.      * @param delta
  28.      *            the delta stream, or at least the header of it.
  29.      * @return the base object's size.
  30.      */
  31.     public static long getBaseSize(byte[] delta) {
  32.         int p = 0;
  33.         long baseLen = 0;
  34.         int c, shift = 0;
  35.         do {
  36.             c = delta[p++] & 0xff;
  37.             baseLen |= ((long) (c & 0x7f)) << shift;
  38.             shift += 7;
  39.         } while ((c & 0x80) != 0);
  40.         return baseLen;
  41.     }

  42.     /**
  43.      * Length of the resulting object in the delta stream.
  44.      *
  45.      * @param delta
  46.      *            the delta stream, or at least the header of it.
  47.      * @return the resulting object's size.
  48.      */
  49.     public static long getResultSize(byte[] delta) {
  50.         int p = 0;

  51.         // Skip length of the base object.
  52.         //
  53.         int c;
  54.         do {
  55.             c = delta[p++] & 0xff;
  56.         } while ((c & 0x80) != 0);

  57.         long resLen = 0;
  58.         int shift = 0;
  59.         do {
  60.             c = delta[p++] & 0xff;
  61.             resLen |= ((long) (c & 0x7f)) << shift;
  62.             shift += 7;
  63.         } while ((c & 0x80) != 0);
  64.         return resLen;
  65.     }

  66.     /**
  67.      * Apply the changes defined by delta to the data in base, yielding a new
  68.      * array of bytes.
  69.      *
  70.      * @param base
  71.      *            some byte representing an object of some kind.
  72.      * @param delta
  73.      *            a git pack delta defining the transform from one version to
  74.      *            another.
  75.      * @return patched base
  76.      */
  77.     public static final byte[] apply(byte[] base, byte[] delta) {
  78.         return apply(base, delta, null);
  79.     }

  80.     /**
  81.      * Apply the changes defined by delta to the data in base, yielding a new
  82.      * array of bytes.
  83.      *
  84.      * @param base
  85.      *            some byte representing an object of some kind.
  86.      * @param delta
  87.      *            a git pack delta defining the transform from one version to
  88.      *            another.
  89.      * @param result
  90.      *            array to store the result into. If null the result will be
  91.      *            allocated and returned.
  92.      * @return either {@code result}, or the result array allocated.
  93.      */
  94.     public static final byte[] apply(final byte[] base, final byte[] delta,
  95.             byte[] result) {
  96.         int deltaPtr = 0;

  97.         // Length of the base object (a variable length int).
  98.         //
  99.         int baseLen = 0;
  100.         int c, shift = 0;
  101.         do {
  102.             c = delta[deltaPtr++] & 0xff;
  103.             baseLen |= (c & 0x7f) << shift;
  104.             shift += 7;
  105.         } while ((c & 0x80) != 0);
  106.         if (base.length != baseLen)
  107.             throw new IllegalArgumentException(
  108.                     JGitText.get().baseLengthIncorrect);

  109.         // Length of the resulting object (a variable length int).
  110.         //
  111.         int resLen = 0;
  112.         shift = 0;
  113.         do {
  114.             c = delta[deltaPtr++] & 0xff;
  115.             resLen |= (c & 0x7f) << shift;
  116.             shift += 7;
  117.         } while ((c & 0x80) != 0);

  118.         if (result == null)
  119.             result = new byte[resLen];
  120.         else if (result.length != resLen)
  121.             throw new IllegalArgumentException(
  122.                     JGitText.get().resultLengthIncorrect);

  123.         int resultPtr = 0;
  124.         while (deltaPtr < delta.length) {
  125.             final int cmd = delta[deltaPtr++] & 0xff;
  126.             if ((cmd & 0x80) != 0) {
  127.                 // Determine the segment of the base which should
  128.                 // be copied into the output. The segment is given
  129.                 // as an offset and a length.
  130.                 //
  131.                 int copyOffset = 0;
  132.                 if ((cmd & 0x01) != 0)
  133.                     copyOffset = delta[deltaPtr++] & 0xff;
  134.                 if ((cmd & 0x02) != 0)
  135.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
  136.                 if ((cmd & 0x04) != 0)
  137.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
  138.                 if ((cmd & 0x08) != 0)
  139.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 24;

  140.                 int copySize = 0;
  141.                 if ((cmd & 0x10) != 0)
  142.                     copySize = delta[deltaPtr++] & 0xff;
  143.                 if ((cmd & 0x20) != 0)
  144.                     copySize |= (delta[deltaPtr++] & 0xff) << 8;
  145.                 if ((cmd & 0x40) != 0)
  146.                     copySize |= (delta[deltaPtr++] & 0xff) << 16;
  147.                 if (copySize == 0)
  148.                     copySize = 0x10000;

  149.                 System.arraycopy(base, copyOffset, result, resultPtr, copySize);
  150.                 resultPtr += copySize;
  151.             } else if (cmd != 0) {
  152.                 // Anything else the data is literal within the delta
  153.                 // itself.
  154.                 //
  155.                 System.arraycopy(delta, deltaPtr, result, resultPtr, cmd);
  156.                 deltaPtr += cmd;
  157.                 resultPtr += cmd;
  158.             } else {
  159.                 // cmd == 0 has been reserved for future encoding but
  160.                 // for now its not acceptable.
  161.                 //
  162.                 throw new IllegalArgumentException(
  163.                         JGitText.get().unsupportedCommand0);
  164.             }
  165.         }

  166.         return result;
  167.     }

  168.     /**
  169.      * Format this delta as a human readable string.
  170.      *
  171.      * @param delta
  172.      *            the delta instruction sequence to format.
  173.      * @return the formatted delta.
  174.      */
  175.     public static String format(byte[] delta) {
  176.         return format(delta, true);
  177.     }

  178.     /**
  179.      * Format this delta as a human readable string.
  180.      *
  181.      * @param delta
  182.      *            the delta instruction sequence to format.
  183.      * @param includeHeader
  184.      *            true if the header (base size and result size) should be
  185.      *            included in the formatting.
  186.      * @return the formatted delta.
  187.      */
  188.     public static String format(byte[] delta, boolean includeHeader) {
  189.         StringBuilder r = new StringBuilder();
  190.         int deltaPtr = 0;

  191.         long baseLen = 0;
  192.         int c, shift = 0;
  193.         do {
  194.             c = delta[deltaPtr++] & 0xff;
  195.             baseLen |= ((long) (c & 0x7f)) << shift;
  196.             shift += 7;
  197.         } while ((c & 0x80) != 0);

  198.         long resLen = 0;
  199.         shift = 0;
  200.         do {
  201.             c = delta[deltaPtr++] & 0xff;
  202.             resLen |= ((long) (c & 0x7f)) << shift;
  203.             shift += 7;
  204.         } while ((c & 0x80) != 0);

  205.         if (includeHeader) {
  206.             r.append("DELTA( BASE="); //$NON-NLS-1$
  207.             r.append(baseLen);
  208.             r.append(" RESULT="); //$NON-NLS-1$
  209.             r.append(resLen);
  210.             r.append(" )\n"); //$NON-NLS-1$
  211.         }

  212.         while (deltaPtr < delta.length) {
  213.             final int cmd = delta[deltaPtr++] & 0xff;
  214.             if ((cmd & 0x80) != 0) {
  215.                 // Determine the segment of the base which should
  216.                 // be copied into the output. The segment is given
  217.                 // as an offset and a length.
  218.                 //
  219.                 int copyOffset = 0;
  220.                 if ((cmd & 0x01) != 0)
  221.                     copyOffset = delta[deltaPtr++] & 0xff;
  222.                 if ((cmd & 0x02) != 0)
  223.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
  224.                 if ((cmd & 0x04) != 0)
  225.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
  226.                 if ((cmd & 0x08) != 0)
  227.                     copyOffset |= (delta[deltaPtr++] & 0xff) << 24;

  228.                 int copySize = 0;
  229.                 if ((cmd & 0x10) != 0)
  230.                     copySize = delta[deltaPtr++] & 0xff;
  231.                 if ((cmd & 0x20) != 0)
  232.                     copySize |= (delta[deltaPtr++] & 0xff) << 8;
  233.                 if ((cmd & 0x40) != 0)
  234.                     copySize |= (delta[deltaPtr++] & 0xff) << 16;
  235.                 if (copySize == 0)
  236.                     copySize = 0x10000;

  237.                 r.append("  COPY  ("); //$NON-NLS-1$
  238.                 r.append(copyOffset);
  239.                 r.append(", "); //$NON-NLS-1$
  240.                 r.append(copySize);
  241.                 r.append(")\n"); //$NON-NLS-1$
  242.             } else if (cmd != 0) {
  243.                 // Anything else the data is literal within the delta
  244.                 // itself.
  245.                 //
  246.                 r.append("  INSERT("); //$NON-NLS-1$
  247.                 r.append(QuotedString.GIT_PATH.quote(RawParseUtils.decode(
  248.                         delta, deltaPtr, deltaPtr + cmd)));
  249.                 r.append(")\n"); //$NON-NLS-1$
  250.                 deltaPtr += cmd;
  251.             } else {
  252.                 // cmd == 0 has been reserved for future encoding but
  253.                 // for now its not acceptable.
  254.                 //
  255.                 throw new IllegalArgumentException(
  256.                         JGitText.get().unsupportedCommand0);
  257.             }
  258.         }

  259.         return r.toString();
  260.     }
  261. }