WorkingTreeIterator: directly filter input stream
This way we can avoid to access the byte buffers backing array. Implement a ByteBufferInputStream to wrap a byte buffer which we can use to expose the filter result as an input stream. Change-Id: I461c82090de2562ea9b649b3f953aad4571e3d25
This commit is contained in:
parent
84ced89dc3
commit
a2bce029aa
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023, SAP SE 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 static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ByteBufferInputStreamTest {
|
||||||
|
|
||||||
|
private static final byte data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
|
||||||
|
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
|
||||||
|
|
||||||
|
private ByteBuffer buf;
|
||||||
|
|
||||||
|
private ByteBufferInputStream is;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
buf = ByteBuffer.wrap(data);
|
||||||
|
is = new ByteBufferInputStream(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead() throws IOException {
|
||||||
|
assertEquals(0x00, is.read());
|
||||||
|
assertEquals(0x01, is.read());
|
||||||
|
assertEquals(0x02, is.read());
|
||||||
|
assertEquals(0x03, is.read());
|
||||||
|
assertEquals(0x04, is.read());
|
||||||
|
assertEquals(0x05, is.read());
|
||||||
|
assertEquals(0x06, is.read());
|
||||||
|
assertEquals(0x07, is.read());
|
||||||
|
assertEquals(0x08, is.read());
|
||||||
|
assertEquals(0x09, is.read());
|
||||||
|
assertEquals(0x0A, is.read());
|
||||||
|
assertEquals(0x0B, is.read());
|
||||||
|
assertEquals(0x0C, is.read());
|
||||||
|
assertEquals(0x0D, is.read());
|
||||||
|
assertEquals(0x0E, is.read());
|
||||||
|
assertEquals(0x0F, is.read());
|
||||||
|
assertEquals(-1, is.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadMultiple() throws IOException {
|
||||||
|
byte[] x = new byte[5];
|
||||||
|
int n = is.read(x);
|
||||||
|
assertEquals(5, n);
|
||||||
|
assertArrayEquals(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadMultipleOffset() throws IOException {
|
||||||
|
byte[] x = new byte[7];
|
||||||
|
int n = is.read(x, 4, 3);
|
||||||
|
assertEquals(3, n);
|
||||||
|
assertArrayEquals(
|
||||||
|
new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02 },
|
||||||
|
x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadAll() throws IOException {
|
||||||
|
byte[] x = is.readAllBytes();
|
||||||
|
assertEquals(16, x.length);
|
||||||
|
assertArrayEquals(data, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMarkReset() throws IOException {
|
||||||
|
byte[] x = new byte[5];
|
||||||
|
int n = is.read(x);
|
||||||
|
assertEquals(11, is.available());
|
||||||
|
assertTrue(is.markSupported());
|
||||||
|
is.mark(is.available());
|
||||||
|
is.reset();
|
||||||
|
byte[] y = new byte[5];
|
||||||
|
int m = is.read(y);
|
||||||
|
assertEquals(n, m);
|
||||||
|
assertArrayEquals(new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClosed() {
|
||||||
|
is.close();
|
||||||
|
Exception e = assertThrows(IOException.class, () -> is.read());
|
||||||
|
assertEquals(JGitText.get().inputStreamClosed, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadNBytes() throws IOException {
|
||||||
|
byte[] x = is.readNBytes(4);
|
||||||
|
assertArrayEquals(new byte[] { 0x00, 0x01, 0x02, 0x03 }, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadNBytesOffset() throws IOException {
|
||||||
|
byte[] x = new byte[10];
|
||||||
|
Arrays.fill(x, (byte) 0x0F);
|
||||||
|
is.readNBytes(x, 3, 4);
|
||||||
|
assertArrayEquals(new byte[] { 0x0F, 0x0F, 0x0F, 0x00, 0x01, 0x02, 0x03,
|
||||||
|
0x0F, 0x0F, 0x0F }, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead0() throws IOException {
|
||||||
|
byte[] x = new byte[7];
|
||||||
|
int n = is.read(x, 4, 0);
|
||||||
|
assertEquals(0, n);
|
||||||
|
|
||||||
|
is.readAllBytes();
|
||||||
|
n = is.read(x, 4, 3);
|
||||||
|
assertEquals(-1, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkip() throws IOException {
|
||||||
|
assertEquals(15, is.skip(15));
|
||||||
|
assertEquals(0x0F, is.read());
|
||||||
|
assertEquals(-1, is.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkip0() throws IOException {
|
||||||
|
assertEquals(0, is.skip(0));
|
||||||
|
assertEquals(0x00, is.read());
|
||||||
|
}
|
||||||
|
}
|
|
@ -389,6 +389,7 @@ initFailedGitDirIsNoDirectory=Cannot set git-dir to ''{0}'' which is not a direc
|
||||||
initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory {0} and separate git-dir {1} specified both folders should not point to the same location
|
initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory {0} and separate git-dir {1} specified both folders should not point to the same location
|
||||||
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
|
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
|
||||||
inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing.
|
inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing.
|
||||||
|
inputStreamClosed=InputStream was closed
|
||||||
inputStreamMustSupportMark=InputStream must support mark()
|
inputStreamMustSupportMark=InputStream must support mark()
|
||||||
integerValueNotInRange=Integer value {0}.{1} = {2} not in range {3}..{4}
|
integerValueNotInRange=Integer value {0}.{1} = {2} not in range {3}..{4}
|
||||||
integerValueNotInRangeSubSection=Integer value {0}.{1}.{2} = {3} not in range {4}..{5}
|
integerValueNotInRangeSubSection=Integer value {0}.{1}.{2} = {3} not in range {4}..{5}
|
||||||
|
|
|
@ -419,6 +419,7 @@ public static JGitText get() {
|
||||||
/***/ public String initFailedNonBareRepoSameDirs;
|
/***/ public String initFailedNonBareRepoSameDirs;
|
||||||
/***/ public String inMemoryBufferLimitExceeded;
|
/***/ public String inMemoryBufferLimitExceeded;
|
||||||
/***/ public String inputDidntMatchLength;
|
/***/ public String inputDidntMatchLength;
|
||||||
|
/***/ public String inputStreamClosed;
|
||||||
/***/ public String inputStreamMustSupportMark;
|
/***/ public String inputStreamMustSupportMark;
|
||||||
/***/ public String integerValueNotInRange;
|
/***/ public String integerValueNotInRange;
|
||||||
/***/ public String integerValueNotInRangeSubSection;
|
/***/ public String integerValueNotInRangeSubSection;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -72,6 +71,7 @@
|
||||||
import org.eclipse.jgit.util.SystemReader;
|
import org.eclipse.jgit.util.SystemReader;
|
||||||
import org.eclipse.jgit.util.TemporaryBuffer;
|
import org.eclipse.jgit.util.TemporaryBuffer;
|
||||||
import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
|
import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
|
||||||
|
import org.eclipse.jgit.util.io.ByteBufferInputStream;
|
||||||
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
|
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
|
||||||
import org.eclipse.jgit.util.sha1.SHA1;
|
import org.eclipse.jgit.util.sha1.SHA1;
|
||||||
|
|
||||||
|
@ -407,9 +407,9 @@ private long possiblyFilteredLength(Entry e, long len) throws IOException {
|
||||||
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
||||||
InputStream is = e.openInputStream();
|
InputStream is = e.openInputStream();
|
||||||
try {
|
try {
|
||||||
ByteBuffer rawbuf = IO.readWholeStream(is, (int) len);
|
ByteBuffer filteredData = IO.readWholeStream(filterClean(is),
|
||||||
rawbuf = filterClean(rawbuf.array(), rawbuf.limit());
|
(int) len);
|
||||||
return rawbuf.limit();
|
return filteredData.remaining();
|
||||||
} finally {
|
} finally {
|
||||||
safeClose(is);
|
safeClose(is);
|
||||||
}
|
}
|
||||||
|
@ -438,10 +438,9 @@ && getEolStreamType(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
||||||
ByteBuffer rawbuf = IO.readWholeStream(is, (int) len);
|
ByteBuffer filteredData = IO.readWholeStream(filterClean(is), (int) len);
|
||||||
rawbuf = filterClean(rawbuf.array(), rawbuf.limit());
|
canonLen = filteredData.remaining();
|
||||||
canonLen = rawbuf.limit();
|
return new ByteBufferInputStream(filteredData);
|
||||||
return new ByteArrayInputStream(rawbuf.array(), 0, (int) canonLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCleanFilterCommand() == null && isBinary(e)) {
|
if (getCleanFilterCommand() == null && isBinary(e)) {
|
||||||
|
@ -477,16 +476,6 @@ private static boolean isBinary(Entry entry) throws IOException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteBuffer filterClean(byte[] src, int n)
|
|
||||||
throws IOException {
|
|
||||||
InputStream in = new ByteArrayInputStream(src);
|
|
||||||
try {
|
|
||||||
return IO.readWholeStream(filterClean(in), n);
|
|
||||||
} finally {
|
|
||||||
safeClose(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream filterClean(InputStream in)
|
private InputStream filterClean(InputStream in)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
in = EolStreamTypeUtil.wrapInputStream(in,
|
in = EolStreamTypeUtil.wrapInputStream(in,
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023, SAP SE 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.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.InvalidMarkException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link InputStream} backed by a {@link ByteBuffer}.
|
||||||
|
*/
|
||||||
|
public class ByteBufferInputStream extends InputStream {
|
||||||
|
|
||||||
|
private ByteBuffer buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ByteBufferInputStream}
|
||||||
|
*
|
||||||
|
* @param buf
|
||||||
|
* the ByteBuffer backing the stream
|
||||||
|
*/
|
||||||
|
public ByteBufferInputStream(@NonNull ByteBuffer buf) {
|
||||||
|
this.buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
nullCheck();
|
||||||
|
if (buf.hasRemaining()) {
|
||||||
|
return buf.get() & 0xFF;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
nullCheck();
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
nullCheck();
|
||||||
|
Objects.checkFromIndexSize(off, len, b.length);
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int length = Math.min(buf.remaining(), len);
|
||||||
|
if (length == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buf.get(b, off, length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] readAllBytes() throws IOException {
|
||||||
|
return readNBytes(buf.remaining());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] readNBytes(int len) throws IOException {
|
||||||
|
int l = Math.min(len, buf.remaining());
|
||||||
|
byte[] b = new byte[l];
|
||||||
|
read(b);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readNBytes(byte[] b, int off, int len) throws IOException {
|
||||||
|
return read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
nullCheck();
|
||||||
|
if (n <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// ByteBuffer index has type int
|
||||||
|
int delta = n > Integer.MAX_VALUE ? buf.remaining()
|
||||||
|
: Math.min((int) n, buf.remaining());
|
||||||
|
buf.position(buf.position() + delta);
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
nullCheck();
|
||||||
|
return buf.remaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
buf = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void mark(int readlimit) {
|
||||||
|
buf.mark();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
try {
|
||||||
|
buf.reset();
|
||||||
|
} catch (InvalidMarkException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void nullCheck() throws IOException {
|
||||||
|
if (buf == null) {
|
||||||
|
throw new IOException(JGitText.get().inputStreamClosed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue