View Javadoc
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  
12  import java.io.IOException;
13  import java.io.OutputStream;
14  
15  import org.eclipse.jgit.util.Base85;
16  
17  /**
18   * An {@link OutputStream} that encodes data for a git binary patch.
19   *
20   * @since 5.12
21   */
22  public class BinaryHunkOutputStream extends OutputStream {
23  
24  	private static final int MAX_BYTES = 52;
25  
26  	private final OutputStream out;
27  
28  	private final byte[] buffer = new byte[MAX_BYTES];
29  
30  	private int pos;
31  
32  	/**
33  	 * Creates a new {@link BinaryHunkOutputStream}.
34  	 *
35  	 * @param out
36  	 *            {@link OutputStream} to write the encoded data to
37  	 */
38  	public BinaryHunkOutputStream(OutputStream out) {
39  		this.out = out;
40  	}
41  
42  	/**
43  	 * Flushes and closes this stream, and closes the underlying
44  	 * {@link OutputStream}.
45  	 */
46  	@Override
47  	public void close() throws IOException {
48  		flush();
49  		out.close();
50  	}
51  
52  	/**
53  	 * Writes any buffered output as a binary patch line to the underlying
54  	 * {@link OutputStream} and flushes that stream, too.
55  	 */
56  	@Override
57  	public void flush() throws IOException {
58  		if (pos > 0) {
59  			encode(buffer, 0, pos);
60  			pos = 0;
61  		}
62  		out.flush();
63  	}
64  
65  	@Override
66  	public void write(int b) throws IOException {
67  		buffer[pos++] = (byte) b;
68  		if (pos == buffer.length) {
69  			encode(buffer, 0, pos);
70  			pos = 0;
71  		}
72  	}
73  
74  	@Override
75  	public void write(byte[] b, int off, int len) throws IOException {
76  		if (len == 0) {
77  			return;
78  		}
79  		int toCopy = len;
80  		int in = off;
81  		if (pos > 0) {
82  			// Fill the buffer
83  			int chunk = Math.min(toCopy, buffer.length - pos);
84  			System.arraycopy(b, in, buffer, pos, chunk);
85  			in += chunk;
86  			pos += chunk;
87  			toCopy -= chunk;
88  			if (pos == buffer.length) {
89  				encode(buffer, 0, pos);
90  				pos = 0;
91  			}
92  			if (toCopy == 0) {
93  				return;
94  			}
95  		}
96  		while (toCopy >= MAX_BYTES) {
97  			encode(b, in, MAX_BYTES);
98  			toCopy -= MAX_BYTES;
99  			in += MAX_BYTES;
100 		}
101 		if (toCopy > 0) {
102 			System.arraycopy(b, in, buffer, 0, toCopy);
103 			pos = toCopy;
104 		}
105 	}
106 
107 	private void encode(byte[] data, int off, int length) throws IOException {
108 		if (length <= 26) {
109 			out.write('A' + length - 1);
110 		} else {
111 			out.write('a' + length - 27);
112 		}
113 		out.write(Base85.encode(data, off, length));
114 		out.write('\n');
115 	}
116 }