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