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