• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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