aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java')
-rw-r--r--src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java
new file mode 100644
index 00000000..c643fea2
--- /dev/null
+++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2023 Code Intelligence GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.code_intelligence.jazzer.mutation.support;
+
+import static com.code_intelligence.jazzer.mutation.support.Preconditions.require;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.util.Objects.requireNonNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Queue;
+
+public final class InputStreamSupport {
+ public static byte[] readAllBytes(InputStream stream) throws IOException {
+ requireNonNull(stream);
+ Queue<byte[]> buffers = new ArrayDeque<>();
+ int arrayLength = 0;
+ outer:
+ while (true) {
+ byte[] buffer = new byte[max(8192, stream.available())];
+ buffers.add(buffer);
+ int off = 0;
+ while (off < buffer.length) {
+ int bytesRead = stream.read(buffer, off, buffer.length - off);
+ if (bytesRead == -1) {
+ break outer;
+ }
+ off += bytesRead;
+ arrayLength += bytesRead;
+ }
+ }
+
+ byte[] result = new byte[arrayLength];
+ int offset = 0;
+ byte[] buffer;
+ int remaining = arrayLength;
+ while ((buffer = buffers.poll()) != null) {
+ int toCopy = min(buffer.length, remaining);
+ System.arraycopy(buffer, 0, result, offset, toCopy);
+ remaining -= toCopy;
+ }
+ return result;
+ }
+
+ private static final InputStream infiniteZerosStream = new ExtendWithNullInputStream();
+
+ /**
+ * @return an infinite stream consisting of 0s
+ */
+ public static InputStream infiniteZeros() {
+ return infiniteZerosStream;
+ }
+
+ /**
+ * @return {@code stream} extended with 0s to an infinite stream
+ */
+ public static InputStream extendWithZeros(InputStream stream) {
+ if (stream instanceof ExtendWithNullInputStream) {
+ return stream;
+ }
+ return new ExtendWithNullInputStream(requireNonNull(stream));
+ }
+
+ public static final class ExtendWithNullInputStream extends InputStream {
+ private static final InputStream ALWAYS_EOF = new ByteArrayInputStream(new byte[0]);
+ private final InputStream stream;
+ private boolean eof;
+
+ private ExtendWithNullInputStream() {
+ this.stream = ALWAYS_EOF;
+ this.eof = true;
+ }
+
+ private ExtendWithNullInputStream(InputStream stream) {
+ this.stream = stream;
+ this.eof = false;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (eof) {
+ return 0;
+ }
+
+ int res = stream.read();
+ if (res != -1) {
+ return res;
+ } else {
+ eof = true;
+ return 0;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (eof) {
+ Arrays.fill(b, off, off + len, (byte) 0);
+ } else {
+ int bytesRead = stream.read(b, off, len);
+ if (bytesRead < len) {
+ eof = true;
+ Arrays.fill(b, max(off, off + bytesRead), off + len, (byte) 0);
+ }
+ }
+ return len;
+ }
+
+ @Override
+ public int available() throws IOException {
+ if (eof) {
+ return Integer.MAX_VALUE;
+ } else {
+ return stream.available();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ stream.close();
+ }
+ }
+
+ /**
+ * @return a stream with the first {@code bytes} bytes of {@code stream}
+ */
+ public static InputStream cap(InputStream stream, long bytes) {
+ requireNonNull(stream);
+ require(bytes >= 0, "bytes must be non-negative");
+ return new CappedInputStream(stream, bytes);
+ }
+
+ private static final class CappedInputStream extends InputStream {
+ private final InputStream stream;
+ private long remaining;
+
+ CappedInputStream(InputStream stream, long remaining) {
+ this.stream = stream;
+ this.remaining = remaining;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (remaining == 0) {
+ return -1;
+ }
+
+ int res = stream.read();
+ if (res != -1) {
+ --remaining;
+ }
+ return res;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (remaining == 0) {
+ return -1;
+ }
+
+ int res = stream.read(b, off, (int) min(len, remaining));
+ if (res != -1) {
+ remaining -= res;
+ }
+ return res;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return (int) min(stream.available(), remaining);
+ }
+
+ @Override
+ public void close() throws IOException {
+ stream.close();
+ }
+ }
+
+ /**
+ * Wraps a given stream with the functionality to detect if it was read exactly.
+ * To do so, the stream must provide an accurate implementation of {@link
+ * InputStream#available()}, hence it's restricted to {@link ByteArrayInputStream} for now.
+ *
+ * @return {@code stream} extended that detects if it was consumed exactly
+ */
+ public static ReadExactlyInputStream extendWithReadExactly(ByteArrayInputStream stream) {
+ return new ReadExactlyInputStream(requireNonNull(stream));
+ }
+
+ public static final class ReadExactlyInputStream extends InputStream {
+ private final InputStream stream;
+ private boolean eof;
+
+ private ReadExactlyInputStream(InputStream stream) {
+ this.stream = stream;
+ this.eof = false;
+ }
+
+ public boolean isConsumedExactly() {
+ try {
+ // Forwards availability check to the underlying ByteInputStream,
+ // which is accurate for the number of available bytes.
+ return !eof && available() == 0;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ int res = stream.read();
+ if (res == -1) {
+ eof = true;
+ }
+ return res;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int read = stream.read(b, off, len);
+ if (read < len) {
+ eof = true;
+ }
+ return read;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return stream.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ stream.close();
+ }
+ }
+
+ private InputStreamSupport() {}
+}