001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.gravity.tomcat;
023
024import java.io.IOException;
025import java.io.InputStream;
026
027/**
028 * An unsynchronized input/output byte buffer that avoids useless byte array copies. 
029 * 
030 * @author Franck
031 */
032public class ByteArrayCometIO extends InputStream implements CometIO {
033
034        private static final byte[] BYTES_0 = new byte[0];
035
036        protected final int initialCapacity;
037        protected byte buf[] = BYTES_0;
038        protected int pos = 0;
039        protected int mark = 0;
040        protected int count = 0;
041        
042        public ByteArrayCometIO() {
043                this(2048);
044        }
045        
046        public ByteArrayCometIO(int initialCapacity) {
047                if (initialCapacity < 1)
048                        throw new IllegalArgumentException("initialCapacity must be > 1: " + initialCapacity);
049                this.initialCapacity = initialCapacity;
050        }
051
052        public int readFully(InputStream is) throws IOException {
053                try {
054                        int b = -1;
055                        
056                        while ((b = is.read()) != -1) {
057                                if (count + 1 >= buf.length) {
058                                        if (buf.length > 0) {
059                                                byte[] tmp = new byte[buf.length << 1];
060                                                System.arraycopy(buf, 0, tmp, 0, buf.length);
061                                                buf = tmp;
062                                        }
063                                        else
064                                                buf = new byte[initialCapacity];
065                                }
066                                buf[count++] = (byte)b;
067                        }
068                        
069                        return count;
070                }
071                finally {
072                        is.close();
073                }
074        }
075
076        public boolean readAvailable(InputStream is) throws IOException {
077                boolean eof = false;
078                
079                try {
080                        int available = -1;
081                        while ((available = is.available()) > 0) {
082        
083                    if (count > 0) {
084                        byte[] newBytes = new byte[available + count + 1];
085                        System.arraycopy(buf, 0, newBytes, 0, count);
086                        buf = newBytes;
087                    }
088                    else
089                        buf = new byte[available + 1];
090        
091                    if (is.read(buf, count, available) != available)
092                        throw new IOException("Could not read available bytes: " + available);
093                    
094                    count += available;
095                }
096                        
097                        int b = is.read();
098                        if (b == -1) {
099                                eof = true;
100                                return false;
101                        }
102                        
103                        buf[buf.length - 1] = (byte)b;
104                        count++;
105                        
106                        return true;
107                }
108                finally {
109                        if (eof)
110                                is.close();
111                }
112        }
113        
114        public InputStream getInputStream() throws IOException {
115                return this;
116        }
117
118        @Override
119        public int read() throws IOException {
120                return (pos < count) ? (buf[pos++] & 0xff) : -1;
121        }
122    
123        @Override
124        public int read(byte b[], int off, int len) {
125        if (b == null)
126            throw new NullPointerException();
127        
128        if (off < 0 || len < 0 || len > b.length - off)
129            throw new IndexOutOfBoundsException();
130
131        if (pos >= count)
132            return -1;
133
134        if (pos + len > count)
135            len = count - pos;
136
137        if (len <= 0)
138            return 0;
139
140        System.arraycopy(buf, pos, b, off, len);
141        pos += len;
142        return len;
143        }
144
145        @Override
146    public long skip(long n) {
147        if (pos + n > count)
148            n = count - pos;
149
150        if (n < 0)
151            return 0;
152
153        pos += n;
154        return n;
155    }
156
157        @Override
158    public int available() {
159        return count - pos;
160    }
161
162        @Override
163    public boolean markSupported() {
164        return true;
165    }
166        
167        @Override
168        public void mark(int readAheadLimit) {
169                mark = pos;
170        }
171
172        @Override
173    public void reset() {
174        pos = mark;
175    }
176
177    @Override
178        public void close() throws IOException {
179    }
180}