1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io.input; 18 19 import static java.lang.Math.min; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.InputStream; 23 import java.util.Objects; 24 25 /** 26 * This is an alternative to {@link java.io.ByteArrayInputStream} 27 * which removes the synchronization overhead for non-concurrent 28 * access; as such this class is not thread-safe. 29 * 30 * @see ByteArrayInputStream 31 * @since 2.7 32 */ 33 //@NotThreadSafe 34 public class UnsynchronizedByteArrayInputStream extends InputStream { 35 36 /** 37 * The end of stream marker. 38 */ 39 public static final int END_OF_STREAM = -1; 40 41 /** 42 * The underlying data buffer. 43 */ 44 private final byte[] data; 45 46 /** 47 * End Of Data. 48 * 49 * Similar to data.length, 50 * i.e. the last readable offset + 1. 51 */ 52 private final int eod; 53 54 /** 55 * Current offset in the data buffer. 56 */ 57 private int offset; 58 59 /** 60 * The current mark (if any). 61 */ 62 private int markedOffset; 63 64 /** 65 * Creates a new byte array input stream. 66 * 67 * @param data the buffer 68 */ UnsynchronizedByteArrayInputStream(final byte[] data)69 public UnsynchronizedByteArrayInputStream(final byte[] data) { 70 this.data = Objects.requireNonNull(data, "data"); 71 this.offset = 0; 72 this.eod = data.length; 73 this.markedOffset = this.offset; 74 } 75 76 /** 77 * Creates a new byte array input stream. 78 * 79 * @param data the buffer 80 * @param offset the offset into the buffer 81 * 82 * @throws IllegalArgumentException if the offset is less than zero 83 */ UnsynchronizedByteArrayInputStream(final byte[] data, final int offset)84 public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset) { 85 Objects.requireNonNull(data, "data"); 86 if (offset < 0) { 87 throw new IllegalArgumentException("offset cannot be negative"); 88 } 89 this.data = data; 90 this.offset = min(offset, data.length > 0 ? data.length : offset); 91 this.eod = data.length; 92 this.markedOffset = this.offset; 93 } 94 95 96 /** 97 * Creates a new byte array input stream. 98 * 99 * @param data the buffer 100 * @param offset the offset into the buffer 101 * @param length the length of the buffer 102 * 103 * @throws IllegalArgumentException if the offset or length less than zero 104 */ UnsynchronizedByteArrayInputStream(final byte[] data, final int offset, final int length)105 public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset, final int length) { 106 if (offset < 0) { 107 throw new IllegalArgumentException("offset cannot be negative"); 108 } 109 if (length < 0) { 110 throw new IllegalArgumentException("length cannot be negative"); 111 } 112 this.data = Objects.requireNonNull(data, "data"); 113 this.offset = min(offset, data.length > 0 ? data.length : offset); 114 this.eod = min(this.offset + length, data.length); 115 this.markedOffset = this.offset; 116 } 117 118 @Override available()119 public int available() { 120 return offset < eod ? eod - offset : 0; 121 } 122 123 @SuppressWarnings("sync-override") 124 @Override mark(final int readlimit)125 public void mark(final int readlimit) { 126 this.markedOffset = this.offset; 127 } 128 129 @Override markSupported()130 public boolean markSupported() { 131 return true; 132 } 133 134 @Override read()135 public int read() { 136 return offset < eod ? data[offset++] & 0xff : END_OF_STREAM; 137 } 138 139 @Override read(final byte[] dest)140 public int read(final byte[] dest) { 141 Objects.requireNonNull(dest, "dest"); 142 return read(dest, 0, dest.length); 143 } 144 145 @Override read(final byte[] dest, final int off, final int len)146 public int read(final byte[] dest, final int off, final int len) { 147 Objects.requireNonNull(dest, "dest"); 148 if (off < 0 || len < 0 || off + len > dest.length) { 149 throw new IndexOutOfBoundsException(); 150 } 151 152 if (offset >= eod) { 153 return END_OF_STREAM; 154 } 155 156 int actualLen = eod - offset; 157 if (len < actualLen) { 158 actualLen = len; 159 } 160 if (actualLen <= 0) { 161 return 0; 162 } 163 System.arraycopy(data, offset, dest, off, actualLen); 164 offset += actualLen; 165 return actualLen; 166 } 167 168 @SuppressWarnings("sync-override") 169 @Override reset()170 public void reset() { 171 this.offset = this.markedOffset; 172 } 173 174 @Override skip(final long n)175 public long skip(final long n) { 176 if (n < 0) { 177 throw new IllegalArgumentException("Skipping backward is not supported"); 178 } 179 180 long actualSkip = eod - offset; 181 if (n < actualSkip) { 182 actualSkip = n; 183 } 184 185 offset = Math.addExact(offset, Math.toIntExact(n)); 186 return actualSkip; 187 } 188 } 189