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.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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 int read(byte[] b, int off, int len) throws IOException {
  53. return super.read(b, off, len);
  54. }
  55. @Override
  56. public void close() throws IOException {
  57. in.close();
  58. buffer = null;
  59. }
  60. private void fillBuffer() throws IOException {
  61. int length = in.read();
  62. if (length < 0) {
  63. pos = length;
  64. buffer = null;
  65. return;
  66. }
  67. lineNumber++;
  68. // Length is encoded with characters, A..Z for 1..26 and a..z for 27..52
  69. if ('A' <= length && length <= 'Z') {
  70. length = length - 'A' + 1;
  71. } else if ('a' <= length && length <= 'z') {
  72. length = length - 'a' + 27;
  73. } else {
  74. throw new StreamCorruptedException(MessageFormat.format(
  75. JGitText.get().binaryHunkInvalidLength,
  76. Integer.valueOf(lineNumber), Integer.toHexString(length)));
  77. }
  78. byte[] encoded = new byte[Base85.encodedLength(length)];
  79. for (int i = 0; i < encoded.length; i++) {
  80. int b = in.read();
  81. if (b < 0 || b == '\n') {
  82. throw new EOFException(MessageFormat.format(
  83. JGitText.get().binaryHunkInvalidLength,
  84. Integer.valueOf(lineNumber)));
  85. }
  86. encoded[i] = (byte) b;
  87. }
  88. // Must be followed by a newline; tolerate EOF.
  89. int b = in.read();
  90. if (b >= 0 && b != '\n') {
  91. throw new StreamCorruptedException(MessageFormat.format(
  92. JGitText.get().binaryHunkMissingNewline,
  93. Integer.valueOf(lineNumber)));
  94. }
  95. try {
  96. buffer = Base85.decode(encoded, length);
  97. } catch (IllegalArgumentException e) {
  98. StreamCorruptedException ex = new StreamCorruptedException(
  99. MessageFormat.format(JGitText.get().binaryHunkDecodeError,
  100. Integer.valueOf(lineNumber)));
  101. ex.initCause(e);
  102. throw ex;
  103. }
  104. pos = 0;
  105. }
  106. }