You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BinaryDelta.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. }