View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.io;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.io.UnsupportedEncodingException;
25  
26  import org.eclipse.jetty.util.StringUtil;
27  
28  /* ------------------------------------------------------------------------------- */
29  /**
30   * 
31   */
32  public class ByteArrayBuffer extends AbstractBuffer
33  {
34      // Set a maximum size to a write for the writeTo method, to ensure that very large content is not
35      // written as a single write (which may fall foul to write timeouts if consumed slowly).
36      final static int MAX_WRITE=Integer.getInteger("org.eclipse.jetty.io.ByteArrayBuffer.MAX_WRITE",128*1024);
37      final protected byte[] _bytes;
38  
39      protected ByteArrayBuffer(int size, int access, boolean isVolatile)
40      {
41          this(new byte[size],0,0,access, isVolatile);
42      }
43      
44      public ByteArrayBuffer(byte[] bytes)
45      {
46          this(bytes, 0, bytes.length, READWRITE);
47      }
48  
49      public ByteArrayBuffer(byte[] bytes, int index, int length)
50      {
51          this(bytes, index, length, READWRITE);
52      }
53  
54      public ByteArrayBuffer(byte[] bytes, int index, int length, int access)
55      {
56          super(READWRITE, NON_VOLATILE);
57          _bytes = bytes;
58          setPutIndex(index + length);
59          setGetIndex(index);
60          _access = access;
61      }
62  
63      public ByteArrayBuffer(byte[] bytes, int index, int length, int access, boolean isVolatile)
64      {
65          super(READWRITE, isVolatile);
66          _bytes = bytes;
67          setPutIndex(index + length);
68          setGetIndex(index);
69          _access = access;
70      }
71  
72      public ByteArrayBuffer(int size)
73      {
74          this(new byte[size], 0, 0, READWRITE);
75          setPutIndex(0);
76      }
77  
78      public ByteArrayBuffer(String value)
79      {
80          super(READWRITE,NON_VOLATILE);
81          _bytes = StringUtil.getBytes(value);
82          setGetIndex(0);
83          setPutIndex(_bytes.length);
84          _access=IMMUTABLE;
85          _string = value;
86      }
87      
88      public ByteArrayBuffer(String value,boolean immutable)
89      {
90          super(READWRITE,NON_VOLATILE);
91          _bytes = StringUtil.getBytes(value);
92          setGetIndex(0);
93          setPutIndex(_bytes.length);
94          if (immutable)
95          {
96              _access=IMMUTABLE;
97              _string = value;
98          }
99      }
100 
101     public ByteArrayBuffer(String value,String encoding) throws UnsupportedEncodingException
102     {
103         super(READWRITE,NON_VOLATILE);
104         _bytes = value.getBytes(encoding);
105         setGetIndex(0);
106         setPutIndex(_bytes.length);
107         _access=IMMUTABLE;
108         _string = value;
109     }
110 
111     public byte[] array()
112     {
113         return _bytes;
114     }
115 
116     public int capacity()
117     {
118         return _bytes.length;
119     }
120     
121     @Override
122     public void compact()
123     {
124         if (isReadOnly()) 
125             throw new IllegalStateException(__READONLY);
126         int s = markIndex() >= 0 ? markIndex() : getIndex();
127         if (s > 0)
128         {
129             int length = putIndex() - s;
130             if (length > 0)
131             {
132                 System.arraycopy(_bytes, s,_bytes, 0, length);
133             }
134             if (markIndex() > 0) setMarkIndex(markIndex() - s);
135             setGetIndex(getIndex() - s);
136             setPutIndex(putIndex() - s);
137         }
138     }
139 
140 
141     @Override
142     public boolean equals(Object obj)
143     {
144         if (obj==this)
145             return true;
146 
147         if (obj == null || !(obj instanceof Buffer)) 
148             return false;
149         
150         if (obj instanceof Buffer.CaseInsensitve)
151             return equalsIgnoreCase((Buffer)obj);
152         
153 
154         Buffer b = (Buffer) obj;
155         
156         // reject different lengths
157         if (b.length() != length()) 
158             return false;
159 
160         // reject AbstractBuffer with different hash value
161         if (_hash != 0 && obj instanceof AbstractBuffer)
162         {
163             AbstractBuffer ab = (AbstractBuffer) obj;
164             if (ab._hash != 0 && _hash != ab._hash) 
165                 return false;
166         }
167 
168         // Nothing for it but to do the hard grind.
169         int get=getIndex();
170         int bi=b.putIndex();
171         for (int i = putIndex(); i-->get;)
172         {
173             byte b1 = _bytes[i];
174             byte b2 = b.peek(--bi);
175             if (b1 != b2) return false;
176         }
177         return true;
178     }
179 
180 
181     @Override
182     public boolean equalsIgnoreCase(Buffer b)
183     {
184         if (b==this)
185             return true;
186         
187         // reject different lengths
188         if (b==null || b.length() != length()) 
189             return false;
190 
191         // reject AbstractBuffer with different hash value
192         if (_hash != 0 && b instanceof AbstractBuffer)
193         {
194             AbstractBuffer ab = (AbstractBuffer) b;
195             if (ab._hash != 0 && _hash != ab._hash) return false;
196         }
197 
198         // Nothing for it but to do the hard grind.
199         int get=getIndex();
200         int bi=b.putIndex();
201         byte[] barray=b.array();
202         if (barray==null)
203         {
204             for (int i = putIndex(); i-->get;)
205             {
206                 byte b1 = _bytes[i];
207                 byte b2 = b.peek(--bi);
208                 if (b1 != b2)
209                 {
210                     if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
211                     if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
212                     if (b1 != b2) return false;
213                 }
214             }
215         }
216         else
217         {
218             for (int i = putIndex(); i-->get;)
219             {
220                 byte b1 = _bytes[i];
221                 byte b2 = barray[--bi];
222                 if (b1 != b2)
223                 {
224                     if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
225                     if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
226                     if (b1 != b2) return false;
227                 }
228             }
229         }
230         return true;
231     }
232 
233     @Override
234     public byte get()
235     {
236         return _bytes[_get++];
237     }
238 
239     @Override
240     public int hashCode()
241     {
242         if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) 
243         {
244             int get=getIndex();
245             for (int i = putIndex(); i-- >get;)
246             {
247                 byte b = _bytes[i];
248                 if ('a' <= b && b <= 'z') 
249                     b = (byte) (b - 'a' + 'A');
250                 _hash = 31 * _hash + b;
251             }
252             if (_hash == 0) 
253                 _hash = -1;
254             _hashGet=_get;
255             _hashPut=_put;
256         }
257         return _hash;
258     }
259     
260     
261     public byte peek(int index)
262     {
263         return _bytes[index];
264     }
265     
266     public int peek(int index, byte[] b, int offset, int length)
267     {
268         int l = length;
269         if (index + l > capacity())
270         {
271             l = capacity() - index;
272             if (l==0)
273                 return -1;
274         }
275         
276         if (l < 0) 
277             return -1;
278         
279         System.arraycopy(_bytes, index, b, offset, l);
280         return l;
281     }
282 
283     public void poke(int index, byte b)
284     {
285         /* 
286         if (isReadOnly()) 
287             throw new IllegalStateException(__READONLY);
288         
289         if (index < 0) 
290             throw new IllegalArgumentException("index<0: " + index + "<0");
291         if (index > capacity())
292                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
293         */
294         _bytes[index] = b;
295     }
296     
297     @Override
298     public int poke(int index, Buffer src)
299     {
300         _hash=0;
301         
302         /* 
303         if (isReadOnly()) 
304             throw new IllegalStateException(__READONLY);
305         if (index < 0) 
306             throw new IllegalArgumentException("index<0: " + index + "<0");
307         */
308         
309         int length=src.length();
310         if (index + length > capacity())
311         {
312             length=capacity()-index;
313             /*
314             if (length<0)
315                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
316             */
317         }
318         
319         byte[] src_array = src.array();
320         if (src_array != null)
321             System.arraycopy(src_array, src.getIndex(), _bytes, index, length);
322         else 
323         {
324             int s=src.getIndex();
325             for (int i=0;i<length;i++)
326                 _bytes[index++]=src.peek(s++);
327         }
328         
329         return length;
330     }
331     
332 
333     @Override
334     public int poke(int index, byte[] b, int offset, int length)
335     {
336         _hash=0;
337         /*
338         if (isReadOnly()) 
339             throw new IllegalStateException(__READONLY);
340         if (index < 0) 
341             throw new IllegalArgumentException("index<0: " + index + "<0");
342         */
343         
344         if (index + length > capacity())
345         {
346             length=capacity()-index;
347             /* if (length<0)
348                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
349             */
350         }
351         
352         System.arraycopy(b, offset, _bytes, index, length);
353         
354         return length;
355     }
356     
357     /* ------------------------------------------------------------ */
358     @Override
359     public void writeTo(OutputStream out)
360         throws IOException
361     {
362         int len=length();
363         if (MAX_WRITE>0 && len>MAX_WRITE)
364         {
365             int off=getIndex();
366             while(len>0)
367             {
368                 int c=len>MAX_WRITE?MAX_WRITE:len;
369                 out.write(_bytes,off,c);
370                 off+=c;
371                 len-=c;
372             }
373         }
374         else
375             out.write(_bytes,getIndex(),len);
376         if (!isImmutable())
377             clear();
378     }
379     
380     /* ------------------------------------------------------------ */
381     @Override
382     public int readFrom(InputStream in,int max) throws IOException
383     {
384         if (max<0||max>space())
385             max=space();
386         int p = putIndex();
387         
388         int len=0, total=0, available=max;
389         while (total<max) 
390         {
391             len=in.read(_bytes,p,available);
392             if (len<0)
393                 break;
394             else if (len>0)
395             {
396                 p += len;
397                 total += len;
398                 available -= len;
399                 setPutIndex(p);
400             }
401             if (in.available()<=0)
402                 break;
403         }
404         if (len<0 && total==0)
405             return -1;
406         return total;
407     }
408 
409     /* ------------------------------------------------------------ */
410     @Override
411     public int space()
412     {
413         return _bytes.length - _put;
414     }
415 
416     
417     /* ------------------------------------------------------------ */
418     /* ------------------------------------------------------------ */
419     /* ------------------------------------------------------------ */
420     public static class CaseInsensitive extends ByteArrayBuffer implements Buffer.CaseInsensitve
421     {
422         public CaseInsensitive(String s)
423         {
424             super(s);
425         }
426         
427         public CaseInsensitive(byte[] b, int o, int l, int rw)
428         {
429             super(b,o,l,rw);
430         }
431 
432         @Override
433         public boolean equals(Object obj)
434         {
435             return obj instanceof Buffer && equalsIgnoreCase((Buffer)obj);
436         }
437         
438     }
439 }