1 /* 2 * Copyright 2023 Code Intelligence GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.code_intelligence.jazzer.mutation.support; 18 19 import static com.code_intelligence.jazzer.mutation.support.Preconditions.require; 20 import static java.lang.Math.max; 21 import static java.lang.Math.min; 22 import static java.util.Objects.requireNonNull; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.util.ArrayDeque; 28 import java.util.Arrays; 29 import java.util.Queue; 30 31 public final class InputStreamSupport { readAllBytes(InputStream stream)32 public static byte[] readAllBytes(InputStream stream) throws IOException { 33 requireNonNull(stream); 34 Queue<byte[]> buffers = new ArrayDeque<>(); 35 int arrayLength = 0; 36 outer: 37 while (true) { 38 byte[] buffer = new byte[max(8192, stream.available())]; 39 buffers.add(buffer); 40 int off = 0; 41 while (off < buffer.length) { 42 int bytesRead = stream.read(buffer, off, buffer.length - off); 43 if (bytesRead == -1) { 44 break outer; 45 } 46 off += bytesRead; 47 arrayLength += bytesRead; 48 } 49 } 50 51 byte[] result = new byte[arrayLength]; 52 int offset = 0; 53 byte[] buffer; 54 int remaining = arrayLength; 55 while ((buffer = buffers.poll()) != null) { 56 int toCopy = min(buffer.length, remaining); 57 System.arraycopy(buffer, 0, result, offset, toCopy); 58 remaining -= toCopy; 59 } 60 return result; 61 } 62 63 private static final InputStream infiniteZerosStream = new ExtendWithNullInputStream(); 64 65 /** 66 * @return an infinite stream consisting of 0s 67 */ infiniteZeros()68 public static InputStream infiniteZeros() { 69 return infiniteZerosStream; 70 } 71 72 /** 73 * @return {@code stream} extended with 0s to an infinite stream 74 */ extendWithZeros(InputStream stream)75 public static InputStream extendWithZeros(InputStream stream) { 76 if (stream instanceof ExtendWithNullInputStream) { 77 return stream; 78 } 79 return new ExtendWithNullInputStream(requireNonNull(stream)); 80 } 81 82 public static final class ExtendWithNullInputStream extends InputStream { 83 private static final InputStream ALWAYS_EOF = new ByteArrayInputStream(new byte[0]); 84 private final InputStream stream; 85 private boolean eof; 86 ExtendWithNullInputStream()87 private ExtendWithNullInputStream() { 88 this.stream = ALWAYS_EOF; 89 this.eof = true; 90 } 91 ExtendWithNullInputStream(InputStream stream)92 private ExtendWithNullInputStream(InputStream stream) { 93 this.stream = stream; 94 this.eof = false; 95 } 96 97 @Override read()98 public int read() throws IOException { 99 if (eof) { 100 return 0; 101 } 102 103 int res = stream.read(); 104 if (res != -1) { 105 return res; 106 } else { 107 eof = true; 108 return 0; 109 } 110 } 111 112 @Override read(byte[] b, int off, int len)113 public int read(byte[] b, int off, int len) throws IOException { 114 if (eof) { 115 Arrays.fill(b, off, off + len, (byte) 0); 116 } else { 117 int bytesRead = stream.read(b, off, len); 118 if (bytesRead < len) { 119 eof = true; 120 Arrays.fill(b, max(off, off + bytesRead), off + len, (byte) 0); 121 } 122 } 123 return len; 124 } 125 126 @Override available()127 public int available() throws IOException { 128 if (eof) { 129 return Integer.MAX_VALUE; 130 } else { 131 return stream.available(); 132 } 133 } 134 135 @Override close()136 public void close() throws IOException { 137 stream.close(); 138 } 139 } 140 141 /** 142 * @return a stream with the first {@code bytes} bytes of {@code stream} 143 */ cap(InputStream stream, long bytes)144 public static InputStream cap(InputStream stream, long bytes) { 145 requireNonNull(stream); 146 require(bytes >= 0, "bytes must be non-negative"); 147 return new CappedInputStream(stream, bytes); 148 } 149 150 private static final class CappedInputStream extends InputStream { 151 private final InputStream stream; 152 private long remaining; 153 CappedInputStream(InputStream stream, long remaining)154 CappedInputStream(InputStream stream, long remaining) { 155 this.stream = stream; 156 this.remaining = remaining; 157 } 158 159 @Override read()160 public int read() throws IOException { 161 if (remaining == 0) { 162 return -1; 163 } 164 165 int res = stream.read(); 166 if (res != -1) { 167 --remaining; 168 } 169 return res; 170 } 171 172 @Override read(byte[] b, int off, int len)173 public int read(byte[] b, int off, int len) throws IOException { 174 if (remaining == 0) { 175 return -1; 176 } 177 178 int res = stream.read(b, off, (int) min(len, remaining)); 179 if (res != -1) { 180 remaining -= res; 181 } 182 return res; 183 } 184 185 @Override available()186 public int available() throws IOException { 187 return (int) min(stream.available(), remaining); 188 } 189 190 @Override close()191 public void close() throws IOException { 192 stream.close(); 193 } 194 } 195 196 /** 197 * Wraps a given stream with the functionality to detect if it was read exactly. 198 * To do so, the stream must provide an accurate implementation of {@link 199 * InputStream#available()}, hence it's restricted to {@link ByteArrayInputStream} for now. 200 * 201 * @return {@code stream} extended that detects if it was consumed exactly 202 */ extendWithReadExactly(ByteArrayInputStream stream)203 public static ReadExactlyInputStream extendWithReadExactly(ByteArrayInputStream stream) { 204 return new ReadExactlyInputStream(requireNonNull(stream)); 205 } 206 207 public static final class ReadExactlyInputStream extends InputStream { 208 private final InputStream stream; 209 private boolean eof; 210 ReadExactlyInputStream(InputStream stream)211 private ReadExactlyInputStream(InputStream stream) { 212 this.stream = stream; 213 this.eof = false; 214 } 215 isConsumedExactly()216 public boolean isConsumedExactly() { 217 try { 218 // Forwards availability check to the underlying ByteInputStream, 219 // which is accurate for the number of available bytes. 220 return !eof && available() == 0; 221 } catch (IOException e) { 222 return false; 223 } 224 } 225 226 @Override read()227 public int read() throws IOException { 228 int res = stream.read(); 229 if (res == -1) { 230 eof = true; 231 } 232 return res; 233 } 234 235 @Override read(byte[] b, int off, int len)236 public int read(byte[] b, int off, int len) throws IOException { 237 int read = stream.read(b, off, len); 238 if (read < len) { 239 eof = true; 240 } 241 return read; 242 } 243 244 @Override available()245 public int available() throws IOException { 246 return stream.available(); 247 } 248 249 @Override close()250 public void close() throws IOException { 251 stream.close(); 252 } 253 } 254 InputStreamSupport()255 private InputStreamSupport() {} 256 } 257