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.

BinaryHunkInputStream.java 2.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.util.io;
  11. import java.io.EOFException;
  12. import java.io.IOException;
  13. import java.io.InputStream;
  14. import java.io.StreamCorruptedException;
  15. import java.text.MessageFormat;
  16. import org.eclipse.jgit.internal.JGitText;
  17. import org.eclipse.jgit.util.Base85;
  18. /**
  19. * A stream that decodes git binary patch data on the fly.
  20. *
  21. * @since 5.12
  22. */
  23. public class BinaryHunkInputStream extends InputStream {
  24. private final InputStream in;
  25. private int lineNumber;
  26. private byte[] buffer;
  27. private int pos = 0;
  28. /**
  29. * Creates a new {@link BinaryHunkInputStream}.
  30. *
  31. * @param in
  32. * {@link InputStream} to read the base-85 encoded patch data
  33. * from
  34. */
  35. public BinaryHunkInputStream(InputStream in) {
  36. this.in = in;
  37. }
  38. @Override
  39. public int read() throws IOException {
  40. if (pos < 0) {
  41. return -1;
  42. }
  43. if (buffer == null || pos == buffer.length) {
  44. fillBuffer();
  45. }
  46. if (pos >= 0) {
  47. return buffer[pos++] & 0xFF;
  48. }
  49. return -1;
  50. }
  51. @Override
  52. public void close() throws IOException {
  53. in.close();
  54. buffer = null;
  55. }
  56. private void fillBuffer() throws IOException {
  57. int length = in.read();
  58. if (length < 0) {
  59. pos = length;
  60. buffer = null;
  61. return;
  62. }
  63. lineNumber++;
  64. // Length is encoded with characters, A..Z for 1..26 and a..z for 27..52
  65. if ('A' <= length && length <= 'Z') {
  66. length = length - 'A' + 1;
  67. } else if ('a' <= length && length <= 'z') {
  68. length = length - 'a' + 27;
  69. } else {
  70. throw new StreamCorruptedException(MessageFormat.format(
  71. JGitText.get().binaryHunkInvalidLength,
  72. Integer.valueOf(lineNumber), Integer.toHexString(length)));
  73. }
  74. byte[] encoded = new byte[Base85.encodedLength(length)];
  75. for (int i = 0; i < encoded.length; i++) {
  76. int b = in.read();
  77. if (b < 0 || b == '\n') {
  78. throw new EOFException(MessageFormat.format(
  79. JGitText.get().binaryHunkInvalidLength,
  80. Integer.valueOf(lineNumber)));
  81. }
  82. encoded[i] = (byte) b;
  83. }
  84. // Must be followed by a newline; tolerate EOF.
  85. int b = in.read();
  86. if (b >= 0 && b != '\n') {
  87. throw new StreamCorruptedException(MessageFormat.format(
  88. JGitText.get().binaryHunkMissingNewline,
  89. Integer.valueOf(lineNumber)));
  90. }
  91. try {
  92. buffer = Base85.decode(encoded, length);
  93. } catch (IllegalArgumentException e) {
  94. StreamCorruptedException ex = new StreamCorruptedException(
  95. MessageFormat.format(JGitText.get().binaryHunkDecodeError,
  96. Integer.valueOf(lineNumber)));
  97. ex.initCause(e);
  98. throw ex;
  99. }
  100. pos = 0;
  101. }
  102. }