    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    public static final int END_OF_STREAM = -1;
    035
    036    /**
    037     * The underlying data buffer.
    038     */
    039    private final byte[] data;
    040
    041    /**
    042     * End Of Data.
    043     *
    044     * Similar to data.length,
    045     * i.e. the last readable offset + 1.
    046     */
    047    private final int eod;
    048
    049    /**
    050     * Current offset in the data buffer.
    051     */
    052    private int offset;
    053
    054    /**
    055     * The current mark (if any).
    056     */
    057    private int markedOffset;
    058
    059    /**
    060     * Creates a new byte array input stream.
    061     *
    062     * @param data the buffer
    063     */
    064    public UnsynchronizedByteArrayInputStream(final byte[] data) {
    065        Objects.requireNonNull(data);
    066        this.data = data;
    067        this.offset = 0;
    068        this.eod = data.length;
    069        this.markedOffset = this.offset;
    070    }
    071
    072    /**
    073     * Creates a new byte array input stream.
    074     *
    075     * @param data the buffer
    076     * @param offset the offset into the buffer
    077     *
    078     * @throws IllegalArgumentException if the offset is less than zero
    079     */
    080    public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset) {
    081        Objects.requireNonNull(data);
    082        if (offset < 0) {
    083            throw new IllegalArgumentException("offset cannot be negative");
    084        }
    085        this.data = data;
    086        this.offset = min(offset, data.length > 0 ? data.length: offset);
    087        this.eod = data.length;
    088        this.markedOffset = this.offset;
    089    }
    090
    091
    092    /**
    093     * Creates a new byte array input stream.
    094     *
    095     * @param data the buffer
    096     * @param offset the offset into the buffer
    097     * @param length the length of the buffer
    098     *
    099     * @throws IllegalArgumentException if the offset or length less than zero
    100     */
    101    public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset, final int length) {
    102        Objects.requireNonNull(data);
    103        if (offset < 0) {
    104            throw new IllegalArgumentException("offset cannot be negative");
    105        }
    106        if (length < 0) {
    107            throw new IllegalArgumentException("length cannot be negative");
    108        }
    109        this.data = data;
    110        this.offset = min(offset, data.length > 0 ? data.length : offset);
    111        this.eod = min(this.offset + length, data.length);
    112        this.markedOffset = this.offset;
    113    }
    114
    115    @Override
    116    public int available() {
    117        return offset < eod ? eod - offset : 0;
    118    }
    119
    120    @Override
    121    public int read() {
    122        return offset < eod ? data[offset++] & 0xff : END_OF_STREAM;
    123    }
    124
    125    @Override
    126    public int read(final byte[] b) {
    127        Objects.requireNonNull(b);
    128        return read(b, 0, b.length);
    129    }
    130
    131    @Override
    132    public int read(final byte[] b, final int off, final int len) {
    133        Objects.requireNonNull(b);
    134        if (off < 0 || len < 0 || off + len > b.length) {
    135            throw new IndexOutOfBoundsException();
    136        }
    137
    138        if (offset >= eod) {
    139            return END_OF_STREAM;
    140        }
    141
    142        int actualLen = eod - offset;
    143        if (len < actualLen) {
    144            actualLen = len;
    145        }
    146        if (actualLen <= 0) {
    147            return 0;
    148        }
    149        System.arraycopy(data, offset, b, off, actualLen);
    150        offset += actualLen;
    151        return actualLen;
    152    }
    153
    154    @Override
    155    public long skip(final long n) {
    156        if(n < 0) {
    157            throw new IllegalArgumentException("Skipping backward is not supported");
    158        }
    159
    160        long actualSkip = eod - offset;
    161        if (n < actualSkip) {
    162            actualSkip = n;
    163        }
    164
    165        offset += actualSkip;
    166        return actualSkip;
    167    }
    168
    169    @Override
    170    public boolean markSupported() {
    171        return true;
    172    }
    173
    174    @SuppressWarnings("sync-override")
    175    @Override
    176    public void mark(final int readlimit) {
    177        this.markedOffset = this.offset;
    178    }
    179
    180    @SuppressWarnings("sync-override")
    181    @Override
    182    public void reset() {
    183        this.offset = this.markedOffset;
    184    }
    185}