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