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
|
||||
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
|
||||
inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing.
|
||||
inputStreamClosed=InputStream was closed
|
||||
inputStreamMustSupportMark=InputStream must support mark()
|
||||
integerValueNotInRange=Integer value {0}.{1} = {2} not in range {3}..{4}
|
||||
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 inMemoryBufferLimitExceeded;
|
||||
/***/ public String inputDidntMatchLength;
|
||||
/***/ public String inputStreamClosed;
|
||||
/***/ public String inputStreamMustSupportMark;
|
||||
/***/ public String integerValueNotInRange;
|
||||
/***/ public String integerValueNotInRangeSubSection;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
@ -72,6 +71,7 @@
|
|||
import org.eclipse.jgit.util.SystemReader;
|
||||
import org.eclipse.jgit.util.TemporaryBuffer;
|
||||
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.sha1.SHA1;
|
||||
|
||||
|
@ -407,9 +407,9 @@ private long possiblyFilteredLength(Entry e, long len) throws IOException {
|
|||
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
||||
InputStream is = e.openInputStream();
|
||||
try {
|
||||
ByteBuffer rawbuf = IO.readWholeStream(is, (int) len);
|
||||
rawbuf = filterClean(rawbuf.array(), rawbuf.limit());
|
||||
return rawbuf.limit();
|
||||
ByteBuffer filteredData = IO.readWholeStream(filterClean(is),
|
||||
(int) len);
|
||||
return filteredData.remaining();
|
||||
} finally {
|
||||
safeClose(is);
|
||||
}
|
||||
|
@ -438,10 +438,9 @@ && getEolStreamType(
|
|||
}
|
||||
|
||||
if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
|
||||
ByteBuffer rawbuf = IO.readWholeStream(is, (int) len);
|
||||
rawbuf = filterClean(rawbuf.array(), rawbuf.limit());
|
||||
canonLen = rawbuf.limit();
|
||||
return new ByteArrayInputStream(rawbuf.array(), 0, (int) canonLen);
|
||||
ByteBuffer filteredData = IO.readWholeStream(filterClean(is), (int) len);
|
||||
canonLen = filteredData.remaining();
|
||||
return new ByteBufferInputStream(filteredData);
|
||||
}
|
||||
|
||||
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)
|
||||
throws IOException {
|
||||
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