BinaryHunkInputStream.java
- /*
- * Copyright (C) 2021, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- package org.eclipse.jgit.util.io;
- import java.io.EOFException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.StreamCorruptedException;
- import java.text.MessageFormat;
- import org.eclipse.jgit.internal.JGitText;
- import org.eclipse.jgit.util.Base85;
- /**
- * A stream that decodes git binary patch data on the fly.
- *
- * @since 5.12
- */
- public class BinaryHunkInputStream extends InputStream {
- private final InputStream in;
- private int lineNumber;
- private byte[] buffer;
- private int pos = 0;
- /**
- * Creates a new {@link BinaryHunkInputStream}.
- *
- * @param in
- * {@link InputStream} to read the base-85 encoded patch data
- * from
- */
- public BinaryHunkInputStream(InputStream in) {
- this.in = in;
- }
- @Override
- public int read() throws IOException {
- if (pos < 0) {
- return -1;
- }
- if (buffer == null || pos == buffer.length) {
- fillBuffer();
- }
- if (pos >= 0) {
- return buffer[pos++] & 0xFF;
- }
- return -1;
- }
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return super.read(b, off, len);
- }
- @Override
- public void close() throws IOException {
- in.close();
- buffer = null;
- }
- private void fillBuffer() throws IOException {
- int length = in.read();
- if (length < 0) {
- pos = length;
- buffer = null;
- return;
- }
- lineNumber++;
- // Length is encoded with characters, A..Z for 1..26 and a..z for 27..52
- if ('A' <= length && length <= 'Z') {
- length = length - 'A' + 1;
- } else if ('a' <= length && length <= 'z') {
- length = length - 'a' + 27;
- } else {
- throw new StreamCorruptedException(MessageFormat.format(
- JGitText.get().binaryHunkInvalidLength,
- Integer.valueOf(lineNumber), Integer.toHexString(length)));
- }
- byte[] encoded = new byte[Base85.encodedLength(length)];
- for (int i = 0; i < encoded.length; i++) {
- int b = in.read();
- if (b < 0 || b == '\r' || b == '\n') {
- throw new EOFException(MessageFormat.format(
- JGitText.get().binaryHunkInvalidLength,
- Integer.valueOf(lineNumber)));
- }
- encoded[i] = (byte) b;
- }
- // Must be followed by a newline; tolerate EOF.
- int b = in.read();
- if (b == '\r') {
- // Be lenient and accept CR-LF, too.
- b = in.read();
- }
- if (b >= 0 && b != '\n') {
- throw new StreamCorruptedException(MessageFormat.format(
- JGitText.get().binaryHunkMissingNewline,
- Integer.valueOf(lineNumber)));
- }
- try {
- buffer = Base85.decode(encoded, length);
- } catch (IllegalArgumentException e) {
- StreamCorruptedException ex = new StreamCorruptedException(
- MessageFormat.format(JGitText.get().binaryHunkDecodeError,
- Integer.valueOf(lineNumber)));
- ex.initCause(e);
- throw ex;
- }
- pos = 0;
- }
- }