// Copyright 2010-2011 Michel Kraemer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.undercouch.bson4jackson.io; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; /** * Works like {@link java.io.BufferedInputStream}, but is not thread-safe and * also uses a a re-usable static buffer provided by {@link StaticBuffers} to * achieve better performance * @author Michel Kraemer */ public class StaticBufferedInputStream extends InputStream { /** * A unique key for the re-usable buffer */ protected static final StaticBuffers.Key BUFFER_KEY = StaticBuffers.Key.BUFFER1; /** * Provides re-usable buffers */ protected final StaticBuffers _staticBuffers; /** * A re-usable buffer */ protected final ByteBuffer _byteBuffer; /** * The raw re-usable buffer */ protected final byte[] _raw; /** * The original unbuffered input stream */ protected final InputStream _in; /** * The current read position */ protected int _pos; /** * The number of bytes in the buffer */ protected int _count; /** * The current marked position. -1 means no mark. */ protected int _mark = -1; /** * Creates a new buffered input stream * @param in the original unbuffered input stream */ public StaticBufferedInputStream(InputStream in) { this(in, 8192); } /** * Creates a new buffered input stream * @param in the original unbuffered input stream * @param size the minimum buffer size */ public StaticBufferedInputStream(InputStream in, int size) { _in = in; _staticBuffers = StaticBuffers.getInstance(); _byteBuffer = _staticBuffers.byteBuffer(BUFFER_KEY, size); _raw = _byteBuffer.array(); } @Override public void close() throws IOException { _staticBuffers.releaseByteBuffer(BUFFER_KEY, _byteBuffer); super.close(); } protected void fill() throws IOException { if (_mark < 0) { //there is no mark _pos = 0; } else if (_pos >= _raw.length) { if (_mark > 0) { int cnt = _pos - _mark; System.arraycopy(_raw, _mark, _raw, 0, cnt); _pos = cnt; _mark = 0; } else { //we've read too many data already _mark = -1; _pos = 0; } } _count = _pos; int n = _in.read(_raw, _pos, _raw.length - _pos); if (n > 0) { _count += n; } } @Override public int read() throws IOException { if (_pos >= _count) { fill(); if (_pos >= _count) { return -1; } } return _raw[_pos++] & 0xff; } @Override public int read(byte[] b, int off, int len) throws IOException { if (len == 0) { return 0; } int read = 0; while (len > 0) { int avail = _count - _pos; if (avail <= 0) { fill(); avail = _count - _pos; if (avail <= 0) { return (read == 0 ? -1 : read); } } int cnt = (avail < len ? avail : len); System.arraycopy(_raw, _pos, b, off, cnt); off += cnt; _pos += cnt; read += cnt; len -= cnt; } return read; } @Override public long skip(long n) throws IOException { if (n <= 0) { return 0; } long avail = _count - _pos; if (avail <= 0) { return _in.skip(n); } long cnt = (avail < n ? avail : n); _pos += cnt; return cnt; } @Override public boolean markSupported() { return true; } @Override public void mark(int marklimit) { _mark = _pos; } @Override public void reset() throws IOException { if (_mark < 0) { throw new IOException("Invalid mark"); } _pos = _mark; } }