001/*
002 *  Copyright 2013 Chris Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.lang.io.buffered;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.nio.ByteBuffer;
021import java.nio.ByteOrder;
022
023/**
024 * A BufferedInputStream where the buffer is provided by a NIO DirectByteBuffer. Use this class as an alternative to {@link java.io.BufferedInputStream}
025 */
026public class DirectBytesBufferedInputStream extends AbstractBufferedInputStream {
027
028    private ByteBuffer buf;
029
030    /**
031     * Create a new instance with the default buffer size
032     * @param in InputStream to be wrapped
033     * @see java.io.BufferedInputStream#BufferedInputStream(InputStream)
034     */
035    public DirectBytesBufferedInputStream(InputStream in) {
036        this(in, DEFAULT_BUFFER_SIZE);
037    }
038
039    /**
040     * Creates a new instance with the given buffer size in bytes.
041     * @param in InputStream to be wrapped
042     * @param size The size of the buffer in bytes
043     * @see java.io.BufferedInputStream#BufferedInputStream(InputStream, int)
044     */
045    public DirectBytesBufferedInputStream(InputStream in, int size) {
046        super(in, size);
047        buf = ByteBuffer.allocateDirect(size);
048    }
049
050    /**
051     * Creates a new instance with the given buffer size in bytes.
052     * @param in InputStream to be wrapped
053     * @param size The size of the buffer in bytes
054     * @param useNativeByteOrder If true, native byte ordering will be used
055     * @see java.io.BufferedInputStream#BufferedInputStream(InputStream, int)
056     */
057    public DirectBytesBufferedInputStream(InputStream in, int size, boolean useNativeByteOrder) {
058        this(in, size);
059        buf.order(ByteOrder.nativeOrder());
060    }
061
062    /**
063     * Creates a new instance with the given buffer size in bytes.
064     * @param in InputStream to be wrapped
065     * @param size The size of the buffer in bytes
066     * @param byteOrder Explicitly configure the byte order to be used.
067     * @see java.io.BufferedInputStream#BufferedInputStream(InputStream, int)
068     */
069    public DirectBytesBufferedInputStream(InputStream in, int size, ByteOrder byteOrder) {
070        this(in, size);
071        buf.order(byteOrder);
072    }
073
074    @Override
075    protected void assertBufferOpen() throws IOException {
076        if (buf == null) {
077            throw new IOException("Buffer was closed");
078        }
079    }
080
081    @Override
082    protected void compact(int markPos, int size) {
083        buf.position(markPos);
084        buf.compact();
085    }
086
087    @Override
088    protected byte[] toArray() {
089        return buf.array();
090    }
091
092    @Override
093    protected void resizeBuffer(int position, int newSize) throws IOException {
094
095        if (newSize > getMarkLimit()) {
096            newSize = getMarkLimit();
097        }
098
099        ByteBuffer nbuf = ByteBuffer.allocateDirect(newSize);
100
101        buf.rewind();
102        nbuf.put(buf);
103        nbuf.position(position);
104
105        if (buf == null) {
106            throw new IOException("Stream closed");
107        }
108
109        doClean(buf);
110        buf = nbuf;
111    }
112
113    @Override
114    protected int limit() {
115        return buf.limit();
116    }
117
118    @Override
119    protected int getInt(int position) throws IOException {
120        assertBufferOpen();
121        return buf.getInt(position);
122    }
123
124    @Override
125    protected void get(byte[] b, int pos, int cnt) {
126        buf.get(b, pos, cnt);
127    }
128
129    @Override
130    protected void clean() {
131
132        if (buf == null) {
133            return;
134        }
135
136        doClean(buf);
137
138        buf = null;
139    }
140
141    @SuppressWarnings("restriction")
142    private void doClean(ByteBuffer buf) {
143
144        if (buf == null) {
145            return;
146        }
147
148        sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buf).cleaner();
149
150        if (cleaner != null) {
151            cleaner.clean();
152        }
153    }
154}