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