1 18 package com.Ostermiller.util; 19 20 import java.io.Reader ; 21 import java.io.Writer ; 22 import java.io.IOException ; 23 24 43 public class CircularCharBuffer { 44 45 50 private final static int DEFAULT_SIZE = 1024; 51 52 57 public final static int INFINITE_SIZE = -1; 58 59 81 protected char[] buffer; 82 87 protected volatile int readPosition = 0; 88 93 protected volatile int writePosition = 0; 94 99 protected volatile int markPosition = 0; 100 106 protected volatile int markSize = 0; 107 112 protected volatile boolean infinite = false; 113 119 protected boolean blockingWrite = true; 120 125 protected Reader reader = new CircularCharBufferReader(); 126 131 protected boolean readerClosed = false; 132 137 protected Writer writer = new CircularCharBufferWriter(); 138 143 protected boolean writerClosed = false; 144 145 152 public void clear(){ 153 synchronized (this){ 154 readPosition = 0; 155 writePosition = 0; 156 markPosition = 0; 157 readerClosed = false; 158 writerClosed = false; 159 } 160 } 161 162 177 public Writer getWriter(){ 178 return writer; 179 } 180 181 192 public Reader getReader(){ 193 return reader; 194 } 195 196 208 public int getAvailable(){ 209 synchronized (this){ 210 return available(); 211 } 212 } 213 214 227 public int getSpaceLeft(){ 228 synchronized (this){ 229 return spaceLeft(); 230 } 231 } 232 233 245 public int getSize(){ 246 synchronized (this){ 247 return buffer.length; 248 } 249 } 250 251 256 private void resize(){ 257 char[] newBuffer = new char[buffer.length * 2]; 258 int marked = marked(); 259 int available = available(); 260 if (markPosition <= writePosition){ 261 int length = writePosition - markPosition; 265 System.arraycopy(buffer, markPosition, newBuffer, 0, length); 266 } else { 267 int length1 = buffer.length - markPosition; 268 System.arraycopy(buffer, markPosition, newBuffer, 0, length1); 269 int length2 = writePosition; 270 System.arraycopy(buffer, 0, newBuffer, length1, length2); 271 } 272 buffer = newBuffer; 273 markPosition = 0; 274 readPosition = marked; 275 writePosition = marked + available; 276 } 277 278 283 private int spaceLeft(){ 284 if (writePosition < markPosition){ 285 return (markPosition - writePosition - 1); 289 } else { 290 return ((buffer.length - 1) - (writePosition - markPosition)); 292 } 293 } 294 295 300 private int available(){ 301 if (readPosition <= writePosition){ 302 return (writePosition - readPosition); 306 } else { 307 return (buffer.length - (readPosition - writePosition)); 309 } 310 } 311 312 317 private int marked(){ 318 if (markPosition <= readPosition){ 319 return (readPosition - markPosition); 323 } else { 324 return (buffer.length - (markPosition - readPosition)); 326 } 327 } 328 329 335 private void ensureMark(){ 336 if (marked() >= markSize){ 337 markPosition = readPosition; 338 markSize = 0; 339 } 340 } 341 342 349 public CircularCharBuffer(){ 350 this (DEFAULT_SIZE, true); 351 } 352 353 370 public CircularCharBuffer(int size){ 371 this (size, true); 372 } 373 374 384 public CircularCharBuffer(boolean blockingWrite){ 385 this (DEFAULT_SIZE, blockingWrite); 386 } 387 388 407 public CircularCharBuffer(int size, boolean blockingWrite){ 408 if (size == INFINITE_SIZE){ 409 buffer = new char[DEFAULT_SIZE]; 410 infinite = true; 411 } else { 412 buffer = new char[size]; 413 infinite = false; 414 } 415 this.blockingWrite = blockingWrite; 416 } 417 418 423 protected class CircularCharBufferReader extends Reader { 424 425 434 public void close() throws IOException { 435 synchronized (CircularCharBuffer.this){ 436 readerClosed = true; 437 } 438 } 439 440 454 public void mark(int readAheadLimit) throws IOException { 455 synchronized (CircularCharBuffer.this){ 456 if (readerClosed) throw new IOException ("Reader has been closed; cannot mark a closed Reader."); 457 if (buffer.length - 1 <= readAheadLimit) throw new IOException ("Cannot mark stream, readAheadLimit bigger than buffer size."); 458 markSize = readAheadLimit; 459 markPosition = readPosition; 460 } 461 } 462 463 470 public boolean markSupported() { 471 return true; 472 } 473 474 485 public int read() throws IOException { 486 while (true){ 487 synchronized (CircularCharBuffer.this){ 488 if (readerClosed) throw new IOException ("Reader has been closed; cannot read from a closed Reader."); 489 int available = available(); 490 if (available > 0){ 491 int result = buffer[readPosition] & 0xffff; 492 readPosition++; 493 if (readPosition == buffer.length){ 494 readPosition = 0; 495 } 496 ensureMark(); 497 return result; 498 } else if (writerClosed){ 499 return -1; 500 } 501 } 502 try { 503 Thread.sleep(100); 504 } catch(Exception x){ 505 throw new IOException ("Blocking read operation interrupted."); 506 } 507 } 508 } 509 510 522 public int read(char[] cbuf) throws IOException { 523 return read(cbuf, 0, cbuf.length); 524 } 525 526 540 public int read(char[] cbuf, int off, int len) throws IOException { 541 while (true){ 542 synchronized (CircularCharBuffer.this){ 543 if (readerClosed) throw new IOException ("Reader has been closed; cannot read from a closed Reader."); 544 int available = available(); 545 if (available > 0){ 546 int length = Math.min(len, available); 547 int firstLen = Math.min(length, buffer.length - readPosition); 548 int secondLen = length - firstLen; 549 System.arraycopy(buffer, readPosition, cbuf, off, firstLen); 550 if (secondLen > 0){ 551 System.arraycopy(buffer, 0, cbuf, off+firstLen, secondLen); 552 readPosition = secondLen; 553 } else { 554 readPosition += length; 555 } 556 if (readPosition == buffer.length) { 557 readPosition = 0; 558 } 559 ensureMark(); 560 return length; 561 } else if (writerClosed){ 562 return -1; 563 } 564 } 565 try { 566 Thread.sleep(100); 567 } catch(Exception x){ 568 throw new IOException ("Blocking read operation interrupted."); 569 } 570 } 571 } 572 573 583 public boolean ready() throws IOException { 584 synchronized (CircularCharBuffer.this){ 585 if (readerClosed) throw new IOException ("Reader has been closed, it is not ready."); 586 return (available() > 0); 587 } 588 } 589 590 600 public void reset() throws IOException { 601 synchronized (CircularCharBuffer.this){ 602 if (readerClosed) throw new IOException ("Reader has been closed; cannot reset a closed Reader."); 603 readPosition = markPosition; 604 } 605 } 606 607 619 public long skip(long n) throws IOException , IllegalArgumentException { 620 while (true){ 621 synchronized (CircularCharBuffer.this){ 622 if (readerClosed) throw new IOException ("Reader has been closed; cannot skip characters on a closed Reader."); 623 int available = available(); 624 if (available > 0){ 625 int length = Math.min((int)n, available); 626 int firstLen = Math.min(length, buffer.length - readPosition); 627 int secondLen = length - firstLen; 628 if (secondLen > 0){ 629 readPosition = secondLen; 630 } else { 631 readPosition += length; 632 } 633 if (readPosition == buffer.length) { 634 readPosition = 0; 635 } 636 return length; 637 } else if (writerClosed){ 638 return 0; 639 } 640 } 641 try { 642 Thread.sleep(100); 643 } catch(Exception x){ 644 throw new IOException ("Blocking read operation interrupted."); 645 } 646 } 647 } 648 } 649 650 658 protected class CircularCharBufferWriter extends Writer { 659 660 672 public void close() throws IOException { 673 synchronized (CircularCharBuffer.this){ 674 if (!writerClosed){ 675 flush(); 676 } 677 writerClosed = true; 678 } 679 } 680 681 688 public void flush() throws IOException { 689 if (writerClosed) throw new IOException ("Writer has been closed; cannot flush a closed Writer."); 690 if (readerClosed) throw new IOException ("Buffer closed by Reader; cannot flush."); 691 } 693 694 707 public void write(char[] cbuf) throws IOException { 708 write(cbuf, 0, cbuf.length); 709 } 710 711 726 public void write(char[] cbuf, int off, int len) throws IOException { 727 while (len > 0){ 728 synchronized (CircularCharBuffer.this){ 729 if (writerClosed) throw new IOException ("Writer has been closed; cannot write to a closed Writer."); 730 if (readerClosed) throw new IOException ("Buffer closed by Reader; cannot write to a closed buffer."); 731 int spaceLeft = spaceLeft(); 732 while (infinite && spaceLeft < len){ 733 resize(); 734 spaceLeft = spaceLeft(); 735 } 736 if (!blockingWrite && spaceLeft < len) throw new BufferOverflowException("CircularCharBuffer is full; cannot write " + len + " characters"); 737 int realLen = Math.min(len, spaceLeft); 738 int firstLen = Math.min(realLen, buffer.length - writePosition); 739 int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1); 740 int written = firstLen + secondLen; 741 if (firstLen > 0){ 742 System.arraycopy(cbuf, off, buffer, writePosition, firstLen); 743 } 744 if (secondLen > 0){ 745 System.arraycopy(cbuf, off+firstLen, buffer, 0, secondLen); 746 writePosition = secondLen; 747 } else { 748 writePosition += written; 749 } 750 if (writePosition == buffer.length) { 751 writePosition = 0; 752 } 753 off += written; 754 len -= written; 755 } 756 if (len > 0){ 757 try { 758 Thread.sleep(100); 759 } catch(Exception x){ 760 throw new IOException ("Waiting for available space in buffer interrupted."); 761 } 762 } 763 } 764 } 765 766 780 public void write(int c) throws IOException { 781 boolean written = false; 782 while (!written){ 783 synchronized (CircularCharBuffer.this){ 784 if (writerClosed) throw new IOException ("Writer has been closed; cannot write to a closed Writer."); 785 if (readerClosed) throw new IOException ("Buffer closed by Reader; cannot write to a closed buffer."); 786 int spaceLeft = spaceLeft(); 787 while (infinite && spaceLeft < 1){ 788 resize(); 789 spaceLeft = spaceLeft(); 790 } 791 if (!blockingWrite && spaceLeft < 1) throw new BufferOverflowException("CircularCharBuffer is full; cannot write 1 character"); 792 if (spaceLeft > 0){ 793 buffer[writePosition] = (char)(c & 0xffff); 794 writePosition++; 795 if (writePosition == buffer.length) { 796 writePosition = 0; 797 } 798 written = true; 799 } 800 } 801 if (!written){ 802 try { 803 Thread.sleep(100); 804 } catch(Exception x){ 805 throw new IOException ("Waiting for available space in buffer interrupted."); 806 } 807 } 808 } 809 } 810 811 824 public void write(String str) throws IOException { 825 write(str, 0, str.length()); 826 } 827 828 843 public void write(String str, int off, int len) throws IOException { 844 while (len > 0){ 845 synchronized (CircularCharBuffer.this){ 846 if (writerClosed) throw new IOException ("Writer has been closed; cannot write to a closed Writer."); 847 if (readerClosed) throw new IOException ("Buffer closed by Reader; cannot write to a closed buffer."); 848 int spaceLeft = spaceLeft(); 849 while (infinite && spaceLeft < len){ 850 resize(); 851 spaceLeft = spaceLeft(); 852 } 853 if (!blockingWrite && spaceLeft < len) throw new BufferOverflowException("CircularCharBuffer is full; cannot write " + len + " characters"); 854 int realLen = Math.min(len, spaceLeft); 855 int firstLen = Math.min(realLen, buffer.length - writePosition); 856 int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1); 857 int written = firstLen + secondLen; 858 for (int i=0; i<firstLen; i++){ 859 buffer[writePosition + i] = str.charAt(off+i); 860 } 861 if (secondLen > 0){ 862 for (int i=0; i<secondLen; i++){ 863 buffer[i] = str.charAt(off+firstLen+i); 864 } 865 writePosition = secondLen; 866 } else { 867 writePosition += written; 868 } 869 if (writePosition == buffer.length) { 870 writePosition = 0; 871 } 872 off += written; 873 len -= written; 874 } 875 if (len > 0){ 876 try { 877 Thread.sleep(100); 878 } catch(Exception x){ 879 throw new IOException ("Waiting for available space in buffer interrupted."); 880 } 881 } 882 } 883 } 884 } 885 } 886 | Popular Tags |