001    /**
002     * Copyright (C) 2009-2013 Barchart, Inc. <http://www.barchart.com/>
003     *
004     * All rights reserved. Licensed under the OSI BSD License.
005     *
006     * http://www.opensource.org/licenses/bsd-license.php
007     */
008    package com.barchart.udt.nio;
009    
010    import java.io.IOException;
011    import java.io.InputStream;
012    import java.nio.ByteBuffer;
013    import java.nio.channels.IllegalBlockingModeException;
014    
015    /**
016     * {@link InputStream} implementation for UDT sockets.
017     */
018    public class NioInputStreamUDT extends InputStream {
019    
020            protected final SocketChannelUDT channel;
021    
022            /**
023             * Creates a new input stream for the specified channel.
024             * 
025             * @param channel
026             *            The UDT socket channel.
027             */
028            protected NioInputStreamUDT(final SocketChannelUDT channel) {
029                    if (channel == null) {
030                            throw new NullPointerException("channel == null");
031                    }
032                    if (!channel.isBlocking()) {
033                            throw new IllegalBlockingModeException();
034                    }
035                    this.channel = channel;
036            }
037    
038            @Override
039            public int read() throws IOException {
040    
041                    /*
042                     * Here's the contract from the JavaDoc on this for SocketChannel:
043                     * 
044                     * A read operation might not fill the buffer, and in fact it might not
045                     * read any bytes at all. Whether or not it does so depends upon the
046                     * nature and state of the channel. A socket channel in non-blocking
047                     * mode, for example, cannot read any more bytes than are immediately
048                     * available from the socket's input buffer; similarly, a file channel
049                     * cannot read any more bytes than remain in the file. It is guaranteed,
050                     * however, that if a channel is in blocking mode and there is at least
051                     * one byte remaining in the buffer then this method will block until at
052                     * least one byte is read.
053                     * 
054                     * Long story short: This UDT InputStream should only ever be created
055                     * when the SocketChannel's in blocking mode, and when it's in blocking
056                     * mode the SocketChannel read call below will block just like we need
057                     * it too.
058                     */
059    
060                    final byte[] data = new byte[1];
061                    read(data);
062                    return data[0];
063            }
064    
065            @Override
066            public int read(final byte[] bytes) throws IOException {
067                    return read(bytes, 0, bytes.length);
068            }
069    
070            @Override
071            public int read(final byte[] bytes, final int off, final int len)
072                            throws IOException {
073    
074                    if (len > bytes.length - off) {
075                            throw new IndexOutOfBoundsException("len > bytes.length - off");
076                    }
077    
078                    final ByteBuffer buffer = ByteBuffer.wrap(bytes);
079                    buffer.position(off);
080                    buffer.limit(off + len);
081    
082                    final int read = channel.read(buffer);
083                    return read;
084            }
085    
086            @Override
087            public long skip(final long n) throws IOException {
088    
089                    final ByteBuffer buffer = ByteBuffer.allocateDirect(32768);
090                    long remaining = n;
091    
092                    while (remaining > 0) {
093    
094                            buffer.limit((int) Math.min(remaining, buffer.capacity()));
095                            final int ret = channel.read(buffer);
096    
097                            if (ret <= 0)
098                                    break;
099    
100                            remaining -= ret;
101                            buffer.rewind();
102                    }
103    
104                    return n - remaining;
105            }
106    
107            @Override
108            public int available() throws IOException {
109                    // This is the default InputStream return value.
110                    // The java/net/SocketInputStream.java implementation delegates to
111                    // the native implementation, which returns 0 on at least some OSes.
112                    return 0;
113            }
114    
115            @Override
116            public void close() throws IOException {
117                    channel.close();
118            }
119    
120            @Override
121            public void mark(final int readlimit) {
122                    throw new UnsupportedOperationException("mark not supported");
123            }
124    
125            @Override
126            public void reset() throws IOException {
127                    throw new UnsupportedOperationException("reset not supported");
128            }
129    
130            @Override
131            public boolean markSupported() {
132                    return false;
133            }
134    
135    }