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.net;
009    
010    import java.io.IOException;
011    import java.io.InputStream;
012    import java.nio.channels.IllegalBlockingModeException;
013    
014    import com.barchart.udt.ErrorUDT;
015    import com.barchart.udt.SocketUDT;
016    
017    /**
018     * {@link InputStream} implementation for UDT sockets.
019     */
020    public class NetInputStreamUDT extends InputStream {
021    
022            protected final SocketUDT socketUDT;
023    
024            /**
025             * 
026             * @param socketUDT
027             *            The UDT socket.
028             */
029            public NetInputStreamUDT(final SocketUDT socketUDT) {
030    
031                    if (!socketUDT.isBlocking()) {
032                            throw new IllegalBlockingModeException();
033                    }
034    
035                    this.socketUDT = socketUDT;
036    
037            }
038    
039            @Override
040            public int read() throws IOException {
041    
042                    /*
043                     * Here's the contract from the JavaDoc on this for SocketChannel:
044                     * 
045                     * A read operation might not fill the buffer, and in fact it might not
046                     * read any bytes at all. Whether or not it does so depends upon the
047                     * nature and state of the channel. A socket channel in non-blocking
048                     * mode, for example, cannot read any more bytes than are immediately
049                     * available from the socket's input buffer; similarly, a file channel
050                     * cannot read any more bytes than remain in the file. It is guaranteed,
051                     * however, that if a channel is in blocking mode and there is at least
052                     * one byte remaining in the buffer then this method will block until at
053                     * least one byte is read.
054                     * 
055                     * Long story short: This UDT InputStream should only ever be created
056                     * when the SocketChannel's in blocking mode, and when it's in blocking
057                     * mode the SocketChannel read call below will block just like we need
058                     * it too.
059                     */
060    
061                    final byte[] data = new byte[1];
062    
063                    final int count = read(data);
064    
065                    assert count == 1;
066    
067                    return data[0];
068    
069            }
070    
071            @Override
072            public int read(final byte[] bytes) throws IOException {
073    
074                    return read(bytes, 0, bytes.length);
075    
076            }
077    
078            @SuppressWarnings("serial")
079            @Override
080            public int read(final byte[] bytes, final int off, final int len)
081                            throws IOException {
082    
083                    final int count = socketUDT.receive(bytes, off, off + len);
084    
085                    if (count > 0) {
086                            assert count <= len;
087                            return count;
088                    }
089    
090                    if (count == 0) {
091                            throw new ExceptionReceiveUDT(socketUDT.id(),
092                                            ErrorUDT.USER_DEFINED_MESSAGE, "UDT receive time out") {
093                            };
094                    }
095    
096                    throw new IllegalStateException("should not happen");
097    
098            }
099    
100            @Override
101            public void close() throws IOException {
102                    socketUDT.close();
103            }
104    
105            @Override
106            public int available() throws IOException {
107                    // This is the default InputStream return value.
108                    // The java/net/SocketInputStream.java implementation delegates to
109                    // the native implementation, which returns 0 on at least some OSes.
110                    return 0;
111            }
112    
113            @Override
114            public long skip(final long numbytes) throws IOException {
115                    if (numbytes <= 0) {
116                            return 0;
117                    }
118                    long n = numbytes;
119                    final int buflen = (int) Math.min(1024, n);
120                    final byte data[] = new byte[buflen];
121                    while (n > 0) {
122                            final int r = read(data, 0, (int) Math.min(buflen, n));
123                            if (r < 0) {
124                                    break;
125                            }
126                            n -= r;
127                    }
128                    return numbytes - n;
129            }
130    
131            @Override
132            public void mark(final int readlimit) {
133                    throw new UnsupportedOperationException("mark not supported");
134            }
135    
136            @Override
137            public void reset() throws IOException {
138                    throw new UnsupportedOperationException("reset not supported");
139            }
140    
141            @Override
142            public boolean markSupported() {
143                    return false;
144            }
145    
146    }