]> source.dussan.org Git - jgit.git/commitdiff
WorkingTreeIterator: directly filter input stream 87/204587/6
authorMatthias Sohn <matthias.sohn@sap.com>
Sat, 23 Sep 2023 23:41:13 +0000 (01:41 +0200)
committerMatthias Sohn <matthias.sohn@sap.com>
Mon, 25 Sep 2023 20:06:13 +0000 (22:06 +0200)
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

org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java [new file with mode: 0644]
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
org.eclipse.jgit/src/org/eclipse/jgit/util/io/ByteBufferInputStream.java [new file with mode: 0644]

diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java
new file mode 100644 (file)
index 0000000..ec9f96e
--- /dev/null
@@ -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());
+       }
+}
index a7a7f18804056b7678e3b3b57e7b14bc062f2349..6def50b17aa4d0051a06addf5b9c0cabaf8b8d36 100644 (file)
@@ -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}
index c3b46a315d0a521870f419c8ab9d8256d84fdbc1..6c2c850d7a8fe14b69874aacbaf960509608c0ee 100644 (file)
@@ -419,6 +419,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String initFailedNonBareRepoSameDirs;
        /***/ public String inMemoryBufferLimitExceeded;
        /***/ public String inputDidntMatchLength;
+       /***/ public String inputStreamClosed;
        /***/ public String inputStreamMustSupportMark;
        /***/ public String integerValueNotInRange;
        /***/ public String integerValueNotInRangeSubSection;
index 98d08466126b4e37c284677eb2d901f4c1a7702b..73a3ddaae7bd555017340526640cf9cdcd736e1c 100644 (file)
@@ -15,7 +15,6 @@ package org.eclipse.jgit.treewalk;
 
 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.RawParseUtils;
 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 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
                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 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
                }
 
                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 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
                }
        }
 
-       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,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ByteBufferInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ByteBufferInputStream.java
new file mode 100644 (file)
index 0000000..9c00329
--- /dev/null
@@ -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);
+               }
+       }
+}