View Javadoc

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  
21  /**
22   * @author gregw
23   *  
24   */
25  public abstract class AbstractBuffer implements Buffer
26  {
27      protected final static String 
28      __IMMUTABLE = "IMMUTABLE", 
29      __READONLY = "READONLY",
30      __READWRITE = "READWRITE", 
31      __VOLATILE = "VOLATILE";
32      
33      protected int _access;
34      protected boolean _volatile;
35  
36      protected int _get;
37      protected int _put;
38      protected int _hash;
39      private int _hashGet;
40      private int _hashPut;
41      private int _mark;
42      protected String _string;
43      private View _view;
44  
45      /**
46       * Constructor for BufferView
47       * 
48       * @param access 0==IMMUTABLE, 1==READONLY, 2==READWRITE
49       */
50      public AbstractBuffer(int access, boolean isVolatile)
51      {
52          if (access == IMMUTABLE && isVolatile)
53                  throw new IllegalArgumentException("IMMUTABLE && VOLATILE");
54          setMarkIndex(-1);
55          _access = access;
56          _volatile = isVolatile;
57      }
58  
59      /*
60       * @see org.mortbay.io.Buffer#toArray()
61       */
62      public byte[] asArray()
63      {
64          byte[] bytes = new byte[length()];
65          byte[] array = array();
66          if (array != null)
67              Portable.arraycopy(array, getIndex(), bytes, 0, bytes.length);
68          else
69              peek(getIndex(), bytes, 0, length());
70          return bytes;
71      }
72  
73      public ByteArrayBuffer duplicate(int access)
74      {
75          Buffer b=this.buffer();
76          if (b instanceof Buffer.CaseInsensitve)
77              return new ByteArrayBuffer.CaseInsensitive(asArray(), 0, length(),access);
78          else
79              return new ByteArrayBuffer(asArray(), 0, length(), access);
80      }
81      
82      /*
83       * @see org.mortbay.io.Buffer#asNonVolatile()
84       */
85      public Buffer asNonVolatileBuffer()
86      {
87          if (!isVolatile()) return this;
88          return duplicate(_access);
89      }
90  
91      public Buffer asImmutableBuffer()
92      {
93          if (isImmutable()) return this;
94          return duplicate(IMMUTABLE);
95      }
96  
97      /*
98       * @see org.mortbay.util.Buffer#asReadOnlyBuffer()
99       */
100     public Buffer asReadOnlyBuffer()
101     {
102         if (isReadOnly()) return this;
103         return new View(this, markIndex(), getIndex(), putIndex(), READONLY);
104     }
105 
106     public Buffer asMutableBuffer()
107     {
108         if (!isImmutable()) return this;
109         
110         Buffer b=this.buffer();
111         if (b.isReadOnly())
112         {
113             return duplicate(READWRITE);
114         }
115         return new View(b, markIndex(), getIndex(), putIndex(), _access);
116     }
117 
118     public Buffer buffer()
119     {
120         return this;
121     }
122 
123     public void clear()
124     {
125         setMarkIndex(-1);
126         setGetIndex(0);
127         setPutIndex(0);
128     }
129 
130     public void compact()
131     {
132         if (isReadOnly()) throw new IllegalStateException(__READONLY);
133         int s = markIndex() >= 0 ? markIndex() : getIndex();
134         if (s > 0)
135         {
136             byte array[] = array();
137             int length = putIndex() - s;
138             if (length > 0)
139             {
140                 if (array != null)
141                     Portable.arraycopy(array(), s, array(), 0, length);
142                 else
143                     poke(0, peek(s, length));
144             }
145             if (markIndex() > 0) setMarkIndex(markIndex() - s);
146             setGetIndex(getIndex() - s);
147             setPutIndex(putIndex() - s);
148         }
149     }
150 
151     public boolean equals(Object obj)
152     {
153         if (obj==this)
154             return true;
155         
156         // reject non buffers;
157         if (obj == null || !(obj instanceof Buffer)) return false;
158         Buffer b = (Buffer) obj;
159 
160         if (this instanceof Buffer.CaseInsensitve ||  b instanceof Buffer.CaseInsensitve)
161             return equalsIgnoreCase(b);
162         
163         // reject different lengths
164         if (b.length() != length()) return false;
165 
166         // reject AbstractBuffer with different hash value
167         if (_hash != 0 && obj instanceof AbstractBuffer)
168         {
169             AbstractBuffer ab = (AbstractBuffer) obj;
170             if (ab._hash != 0 && _hash != ab._hash) return false;
171         }
172 
173         // Nothing for it but to do the hard grind.
174         for (int i = length(); i-- > 0;)
175         {
176             byte b1 = peek(getIndex() + i);
177             byte b2 = b.peek(b.getIndex() + i);
178             if (b1 != b2) return false;
179         }
180         return true;
181     }
182 
183     public boolean equalsIgnoreCase(Buffer b)
184     {
185         if (b==this)
186             return true;
187         
188         // reject different lengths
189         if (b.length() != length()) 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         for (int i = length(); i-- > 0;)
200         {
201             byte b1 = peek(getIndex() + i);
202             byte b2 = b.peek(b.getIndex() + i);
203             if (b1 != b2)
204             {
205                 if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
206                 if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
207                 if (b1 != b2) return false;
208             }
209         }
210         return true;
211     }
212 
213     public byte get()
214     {
215         return peek(_get++);
216     }
217 
218     public int get(byte[] b, int offset, int length)
219     {
220         int gi = getIndex();
221         int l=length();
222         if (l==0)
223             return -1;
224         
225         if (length>l)
226             length=l;
227         
228         length = peek(gi, b, offset, length);
229         if (length>0)
230             setGetIndex(gi + length);
231         return length;
232     }
233 
234     public Buffer get(int length)
235     {
236         int gi = getIndex();
237         Buffer view = peek(gi, length);
238         setGetIndex(gi + length);
239         return view;
240     }
241 
242     public final int getIndex()
243     {
244         return _get;
245     }
246 
247     public boolean hasContent()
248     {
249         return _put > _get;
250     }
251     
252     public int hashCode()
253     {
254         if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) 
255         {
256             for (int i = putIndex(); i-- > getIndex();)
257             {
258                 byte b = peek(i);
259                 if ('a' <= b && b <= 'z') b = (byte) (b - 'a' + 'A');
260                 _hash = 31 * _hash + b;
261             }
262             if (_hash == 0) _hash = -1;
263             _hashGet=_get;
264             _hashPut=_put;
265         }
266         return _hash;
267     }
268 
269     public boolean isImmutable()
270     {
271         return _access <= IMMUTABLE;
272     }
273 
274     public boolean isReadOnly()
275     {
276         return _access <= READONLY;
277     }
278 
279     public boolean isVolatile()
280     {
281         return _volatile;
282     }
283 
284     public int length()
285     {
286         return _put - _get;
287     }
288 
289     public void mark()
290     {
291         setMarkIndex(_get - 1);
292     }
293 
294     public void mark(int offset)
295     {
296         setMarkIndex(_get + offset);
297     }
298 
299     public int markIndex()
300     {
301         return _mark;
302     }
303 
304     public byte peek()
305     {
306         return peek(_get);
307     }
308 
309     public Buffer peek(int index, int length)
310     {
311         if (_view == null)
312         {
313             _view = new View(this, -1, index, index + length, isReadOnly() ? READONLY : READWRITE);
314         }
315         else
316         {
317             _view.update(this.buffer());
318             _view.setMarkIndex(-1);
319             _view.setGetIndex(0);
320             _view.setPutIndex(index + length);
321             _view.setGetIndex(index);
322             
323         }
324         return _view;
325     }
326 
327     public int poke(int index, Buffer src)
328     {
329         _hash=0;
330         if (isReadOnly()) throw new IllegalStateException(__READONLY);
331         if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
332         
333         int length=src.length();
334         if (index + length > capacity())
335         {
336             length=capacity()-index;
337             if (length<0)
338                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
339         }
340         
341         byte[] src_array = src.array();
342         byte[] dst_array = array();
343         if (src_array != null && dst_array != null)
344             Portable.arraycopy(src_array, src.getIndex(), dst_array, index, length);
345         else if (src_array != null)
346         {
347             int s=src.getIndex();
348             for (int i=0;i<length;i++)
349                 poke(index++,src_array[s++]);
350         }
351         else if (dst_array != null)
352         {
353             int s=src.getIndex();
354             for (int i=0;i<length;i++)
355                 dst_array[index++]=src.peek(s++);
356         }
357         else
358         {
359             int s=src.getIndex();
360             for (int i=0;i<length;i++)
361                 poke(index++,src.peek(s++));
362         }
363         
364         return length;
365     }
366     
367 
368     public int poke(int index, byte[] b, int offset, int length)
369     {
370         _hash=0;
371         if (isReadOnly()) throw new IllegalStateException(__READONLY);
372         if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
373 
374         if (index + length > capacity())
375         {
376             length=capacity()-index;
377             if (length<0)
378                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
379         }
380         
381         byte[] dst_array = array();
382         if (dst_array != null)
383             Portable.arraycopy(b, offset, dst_array, index, length);
384         else
385         {
386             int s=offset;
387             for (int i=0;i<length;i++)
388                 poke(index++,b[s++]);
389         }
390         return length;
391     }
392 
393     public int put(Buffer src)
394     {
395         int pi = putIndex();
396         int l=poke(pi, src);
397         setPutIndex(pi + l);
398         return l;
399     }
400 
401     public void put(byte b)
402     {
403         int pi = putIndex();
404         poke(pi, b);
405         setPutIndex(pi + 1);
406     }
407 
408     public int put(byte[] b, int offset, int length)
409     {
410         int pi = putIndex();
411         int l = poke(pi, b, offset, length);
412         setPutIndex(pi + l);
413         return l;
414     }
415     
416     public int put(byte[] b)
417     {
418         int pi = putIndex();
419         int l = poke(pi, b, 0, b.length);
420         setPutIndex(pi + l);
421         return l;
422     }
423 
424     public final int putIndex()
425     {
426         return _put;
427     }
428 
429     public void reset()
430     {
431         if (markIndex() >= 0) setGetIndex(markIndex());
432     }
433 
434     public void rewind()
435     {
436         setGetIndex(0);
437         setMarkIndex(-1);
438     }
439 
440 
441     public void setGetIndex(int getIndex)
442     {
443         /* bounds checking
444         if (isImmutable()) new throw IllegalStateException(__IMMUTABLE);
445         if (getIndex < 0) throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0");
446         if (getIndex > putIndex()) throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex());
447          */
448         _get = getIndex;
449         _hash=0;
450     }
451 
452     public void setMarkIndex(int index)
453     {
454         if (index>=0 && isImmutable()) throw new IllegalStateException(__IMMUTABLE);
455         _mark = index;
456     }
457 
458     public void setPutIndex(int putIndex)
459     {
460         /* bounds checking
461         if (isImmutable()) new throw IllegalStateException(__IMMUTABLE);
462         if (putIndex > capacity())
463                 throw new IllegalArgumentException("putIndex>capacity: " + putIndex + ">" + capacity());
464         if (getIndex() > putIndex)
465                 throw new IllegalArgumentException("getIndex>putIndex: " + getIndex() + ">" + putIndex);
466          */
467         _put = putIndex;
468         _hash=0;
469     }
470 
471     public int skip(int n)
472     {
473         if (length() < n) n = length();
474         setGetIndex(getIndex() + n);
475         return n;
476     }
477 
478     public Buffer slice()
479     {
480         return peek(getIndex(), length());
481     }
482 
483     public Buffer sliceFromMark()
484     {
485         return sliceFromMark(getIndex() - markIndex() - 1);
486     }
487 
488     public Buffer sliceFromMark(int length)
489     {
490         if (markIndex() < 0) return null;
491         Buffer view = peek(markIndex(), length);
492         setMarkIndex(-1);
493         return view;
494     }
495 
496     public int space()
497     {
498         return capacity() - putIndex();
499     }
500 
501     public String toDetailString()
502     {
503         StringBuffer buf = new StringBuffer();
504         buf.append("[");
505         buf.append(super.hashCode());
506         buf.append(",");
507         buf.append(this.array().hashCode());
508         buf.append(",m=");
509         buf.append(markIndex());
510         buf.append(",g=");
511         buf.append(getIndex());
512         buf.append(",p=");
513         buf.append(putIndex());
514         buf.append(",c=");
515         buf.append(capacity());
516         buf.append("]={");
517         if (markIndex() >= 0)
518         {
519             for (int i = markIndex(); i < getIndex(); i++)
520             {
521                 char c = (char) peek(i);
522                 if (Character.isISOControl(c))
523                 {
524                     buf.append(c < 16 ? "\\0" : "\\");
525                     buf.append(Integer.toString(c, 16));
526                 }
527                 else
528                     buf.append(c);
529             }
530             buf.append("}{");
531         }
532         int count = 0;
533         for (int i = getIndex(); i < putIndex(); i++)
534         {
535             char c = (char) peek(i);
536             if (Character.isISOControl(c))
537             {
538                 buf.append(c < 16 ? "\\0" : "\\");
539                 buf.append(Integer.toString(c, 16));
540             }
541             else
542                 buf.append(c);
543             if (count++ == 50)
544             {
545                 if (putIndex() - i > 20)
546                 {
547                     buf.append(" ... ");
548                     i = putIndex() - 20;
549                 }
550             }
551         }
552         buf.append('}');
553         return buf.toString();
554     }
555 
556     /* ------------------------------------------------------------ */
557     public String toString()
558     {
559         if (isImmutable())
560         {
561             if (_string == null) 
562                 _string = new String(asArray(), 0, length());
563             return _string;
564         }
565         return new String(asArray(), 0, length());
566     }
567 
568     /* ------------------------------------------------------------ */
569     public String toDebugString()
570     {
571         return getClass()+"@"+super.hashCode();
572     }
573 
574     /* ------------------------------------------------------------ */
575     public void writeTo(OutputStream out)
576     	throws IOException
577     {
578         byte[] array = array();
579         
580         if (array!=null)
581         {
582             out.write(array,getIndex(),length());
583         }
584         else
585         {
586             int len = this.length();
587             byte[] buf=new byte[len>1024?1024:len];
588             int offset=_get;
589             while (len>0)
590             {
591                 int l=peek(offset,buf,0,len>buf.length?buf.length:len);
592                 out.write(buf,0,l);
593                 offset+=l;
594                 len-=l;
595             }
596         } 
597         clear();
598     }
599     
600     /* ------------------------------------------------------------ */
601     public int readFrom(InputStream in,int max) throws IOException
602     {
603         byte[] array = array();
604         int s=space();
605         if (s>max)
606             s=max;
607 
608         if (array!=null)
609         {
610             int l=in.read(array,_put,s);
611             if (l>0)
612                 _put+=l;
613             return l;
614         }
615         else
616         {
617             byte[] buf=new byte[s>1024?1024:s];
618             int total=0;
619             while (s>0)
620             {
621                 int l=in.read(buf,0,buf.length);
622                 if (l<0)
623                     return total>0?total:-1;
624                 int p=put(buf,0,l);
625                 assert l==p;
626                 s-=l;
627             }
628             return total; 
629         }
630     }
631 }