001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.input; 018 019import java.io.InputStream; 020import java.util.Objects; 021 022import static java.lang.Math.min; 023 024/** 025 * This is an alternative to {@link java.io.ByteArrayInputStream} 026 * which removes the synchronization overhead for non-concurrent 027 * access; as such this class is not thread-safe. 028 * 029 * @since 2.7 030 */ 031//@NotThreadSafe 032public class UnsynchronizedByteArrayInputStream extends InputStream { 033 034 /** 035 * The end of stream marker. 036 */ 037 public static final int END_OF_STREAM = -1; 038 039 /** 040 * The underlying data buffer. 041 */ 042 private final byte[] data; 043 044 /** 045 * End Of Data. 046 * 047 * Similar to data.length, 048 * i.e. the last readable offset + 1. 049 */ 050 private final int eod; 051 052 /** 053 * Current offset in the data buffer. 054 */ 055 private int offset; 056 057 /** 058 * The current mark (if any). 059 */ 060 private int markedOffset; 061 062 /** 063 * Creates a new byte array input stream. 064 * 065 * @param data the buffer 066 */ 067 public UnsynchronizedByteArrayInputStream(final byte[] data) { 068 this.data = Objects.requireNonNull(data, "data"); 069 this.offset = 0; 070 this.eod = data.length; 071 this.markedOffset = this.offset; 072 } 073 074 /** 075 * Creates a new byte array input stream. 076 * 077 * @param data the buffer 078 * @param offset the offset into the buffer 079 * 080 * @throws IllegalArgumentException if the offset is less than zero 081 */ 082 public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset) { 083 Objects.requireNonNull(data, "data"); 084 if (offset < 0) { 085 throw new IllegalArgumentException("offset cannot be negative"); 086 } 087 this.data = data; 088 this.offset = min(offset, data.length > 0 ? data.length : offset); 089 this.eod = data.length; 090 this.markedOffset = this.offset; 091 } 092 093 094 /** 095 * Creates a new byte array input stream. 096 * 097 * @param data the buffer 098 * @param offset the offset into the buffer 099 * @param length the length of the buffer 100 * 101 * @throws IllegalArgumentException if the offset or length less than zero 102 */ 103 public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset, final int length) { 104 if (offset < 0) { 105 throw new IllegalArgumentException("offset cannot be negative"); 106 } 107 if (length < 0) { 108 throw new IllegalArgumentException("length cannot be negative"); 109 } 110 this.data = Objects.requireNonNull(data, "data"); 111 this.offset = min(offset, data.length > 0 ? data.length : offset); 112 this.eod = min(this.offset + length, data.length); 113 this.markedOffset = this.offset; 114 } 115 116 @Override 117 public int available() { 118 return offset < eod ? eod - offset : 0; 119 } 120 121 @Override 122 public int read() { 123 return offset < eod ? data[offset++] & 0xff : END_OF_STREAM; 124 } 125 126 @Override 127 public int read(final byte[] dest) { 128 Objects.requireNonNull(dest, "dest"); 129 return read(dest, 0, dest.length); 130 } 131 132 @Override 133 public int read(final byte[] dest, final int off, final int len) { 134 Objects.requireNonNull(dest, "dest"); 135 if (off < 0 || len < 0 || off + len > dest.length) { 136 throw new IndexOutOfBoundsException(); 137 } 138 139 if (offset >= eod) { 140 return END_OF_STREAM; 141 } 142 143 int actualLen = eod - offset; 144 if (len < actualLen) { 145 actualLen = len; 146 } 147 if (actualLen <= 0) { 148 return 0; 149 } 150 System.arraycopy(data, offset, dest, off, actualLen); 151 offset += actualLen; 152 return actualLen; 153 } 154 155 @Override 156 public long skip(final long n) { 157 if (n < 0) { 158 throw new IllegalArgumentException("Skipping backward is not supported"); 159 } 160 161 long actualSkip = eod - offset; 162 if (n < actualSkip) { 163 actualSkip = n; 164 } 165 166 offset += actualSkip; 167 return actualSkip; 168 } 169 170 @Override 171 public boolean markSupported() { 172 return true; 173 } 174 175 @SuppressWarnings("sync-override") 176 @Override 177 public void mark(final int readlimit) { 178 this.markedOffset = this.offset; 179 } 180 181 @SuppressWarnings("sync-override") 182 @Override 183 public void reset() { 184 this.offset = this.markedOffset; 185 } 186}