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