001: /*
002: * Circular Byte Buffer
003: * Copyright (C) 2002 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * See COPYING.TXT for details.
017: */
018: package com.Ostermiller.util;
019:
020: import java.io.*;
021:
022: /**
023: * Implements the Circular Buffer producer/consumer model for bytes.
024: * More information about this class is available from <a target="_top" href=
025: * "http://ostermiller.org/utils/CircularByteBuffer.html">ostermiller.org</a>.
026: * <p>
027: * Using this class is a simpler alternative to using a PipedInputStream
028: * and a PipedOutputStream. PipedInputStreams and PipedOutputStreams don't support the
029: * mark operation, don't allow you to control buffer sizes that they use,
030: * and have a more complicated API that requires instantiating two
031: * classes and connecting them.
032: * <p>
033: * This class is thread safe.
034: *
035: * @see CircularCharBuffer
036: * @see CircularObjectBuffer
037: *
038: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
039: * @since ostermillerutils 1.00.00
040: */
041: public class CircularByteBuffer {
042:
043: /**
044: * The default size for a circular byte buffer.
045: *
046: * @since ostermillerutils 1.00.00
047: */
048: private final static int DEFAULT_SIZE = 1024;
049:
050: /**
051: * A buffer that will grow as things are added.
052: *
053: * @since ostermillerutils 1.00.00
054: */
055: public final static int INFINITE_SIZE = -1;
056:
057: /**
058: * The circular buffer.
059: * <p>
060: * The actual capacity of the buffer is one less than the actual length
061: * of the buffer so that an empty and a full buffer can be
062: * distinguished. An empty buffer will have the markPostion and the
063: * writePosition equal to each other. A full buffer will have
064: * the writePosition one less than the markPostion.
065: * <p>
066: * There are three important indexes into the buffer:
067: * The readPosition, the writePosition, and the markPosition.
068: * If the InputStream has never been marked, the readPosition and
069: * the markPosition should always be the same. The bytes
070: * available to be read go from the readPosition to the writePosition,
071: * wrapping around the end of the buffer. The space available for writing
072: * goes from the write position to one less than the markPosition,
073: * wrapping around the end of the buffer. The bytes that have
074: * been saved to support a reset() of the InputStream go from markPosition
075: * to readPosition, wrapping around the end of the buffer.
076: *
077: * @since ostermillerutils 1.00.00
078: */
079: protected byte[] buffer;
080: /**
081: * Index of the first byte available to be read.
082: *
083: * @since ostermillerutils 1.00.00
084: */
085: protected volatile int readPosition = 0;
086: /**
087: * Index of the first byte available to be written.
088: *
089: * @since ostermillerutils 1.00.00
090: */
091: protected volatile int writePosition = 0;
092: /**
093: * Index of the first saved byte. (To support stream marking.)
094: *
095: * @since ostermillerutils 1.00.00
096: */
097: protected volatile int markPosition = 0;
098: /**
099: * Number of bytes that have to be saved
100: * to support mark() and reset() on the InputStream.
101: *
102: * @since ostermillerutils 1.00.00
103: */
104: protected volatile int markSize = 0;
105: /**
106: * If this buffer is infinite (should resize itself when full)
107: *
108: * @since ostermillerutils 1.00.00
109: */
110: protected volatile boolean infinite = false;
111: /**
112: * True if a write to a full buffer should block until the buffer
113: * has room, false if the write method should throw an IOException
114: *
115: * @since ostermillerutils 1.00.00
116: */
117: protected boolean blockingWrite = true;
118: /**
119: * The InputStream that can empty this buffer.
120: *
121: * @since ostermillerutils 1.00.00
122: */
123: protected InputStream in = new CircularByteBufferInputStream();
124: /**
125: * true if the close() method has been called on the InputStream
126: *
127: * @since ostermillerutils 1.00.00
128: */
129: protected boolean inputStreamClosed = false;
130: /**
131: * The OutputStream that can fill this buffer.
132: *
133: * @since ostermillerutils 1.00.00
134: */
135: protected OutputStream out = new CircularByteBufferOutputStream();
136: /**
137: * true if the close() method has been called on the OutputStream
138: *
139: * @since ostermillerutils 1.00.00
140: */
141: protected boolean outputStreamClosed = false;
142:
143: /**
144: * Make this buffer ready for reuse. The contents of the buffer
145: * will be cleared and the streams associated with this buffer
146: * will be reopened if they had been closed.
147: *
148: * @since ostermillerutils 1.00.00
149: */
150: public void clear() {
151: synchronized (this ) {
152: readPosition = 0;
153: writePosition = 0;
154: markPosition = 0;
155: outputStreamClosed = false;
156: inputStreamClosed = false;
157: }
158: }
159:
160: /**
161: * Retrieve a OutputStream that can be used to fill
162: * this buffer.
163: * <p>
164: * Write methods may throw a BufferOverflowException if
165: * the buffer is not large enough. A large enough buffer
166: * size must be chosen so that this does not happen or
167: * the caller must be prepared to catch the exception and
168: * try again once part of the buffer has been consumed.
169: *
170: *
171: * @return the producer for this buffer.
172: *
173: * @since ostermillerutils 1.00.00
174: */
175: public OutputStream getOutputStream() {
176: return out;
177: }
178:
179: /**
180: * Retrieve a InputStream that can be used to empty
181: * this buffer.
182: * <p>
183: * This InputStream supports marks at the expense
184: * of the buffer size.
185: *
186: * @return the consumer for this buffer.
187: *
188: * @since ostermillerutils 1.00.00
189: */
190: public InputStream getInputStream() {
191: return in;
192: }
193:
194: /**
195: * Get number of bytes that are available to be read.
196: * <p>
197: * Note that the number of bytes available plus
198: * the number of bytes free may not add up to the
199: * capacity of this buffer, as the buffer may reserve some
200: * space for other purposes.
201: *
202: * @return the size in bytes of this buffer
203: *
204: * @since ostermillerutils 1.00.00
205: */
206: public int getAvailable() {
207: synchronized (this ) {
208: return available();
209: }
210: }
211:
212: /**
213: * Get the number of bytes this buffer has free for
214: * writing.
215: * <p>
216: * Note that the number of bytes available plus
217: * the number of bytes free may not add up to the
218: * capacity of this buffer, as the buffer may reserve some
219: * space for other purposes.
220: *
221: * @return the available space in bytes of this buffer
222: *
223: * @since ostermillerutils 1.00.00
224: */
225: public int getSpaceLeft() {
226: synchronized (this ) {
227: return spaceLeft();
228: }
229: }
230:
231: /**
232: * Get the capacity of this buffer.
233: * <p>
234: * Note that the number of bytes available plus
235: * the number of bytes free may not add up to the
236: * capacity of this buffer, as the buffer may reserve some
237: * space for other purposes.
238: *
239: * @return the size in bytes of this buffer
240: *
241: * @since ostermillerutils 1.00.00
242: */
243: public int getSize() {
244: synchronized (this ) {
245: return buffer.length;
246: }
247: }
248:
249: /**
250: * double the size of the buffer
251: *
252: * @since ostermillerutils 1.00.00
253: */
254: private void resize() {
255: byte[] newBuffer = new byte[buffer.length * 2];
256: int marked = marked();
257: int available = available();
258: if (markPosition <= writePosition) {
259: // any space between the mark and
260: // the first write needs to be saved.
261: // In this case it is all in one piece.
262: int length = writePosition - markPosition;
263: System
264: .arraycopy(buffer, markPosition, newBuffer, 0,
265: length);
266: } else {
267: int length1 = buffer.length - markPosition;
268: System.arraycopy(buffer, markPosition, newBuffer, 0,
269: length1);
270: int length2 = writePosition;
271: System.arraycopy(buffer, 0, newBuffer, length1, length2);
272: }
273: buffer = newBuffer;
274: markPosition = 0;
275: readPosition = marked;
276: writePosition = marked + available;
277: }
278:
279: /**
280: * Space available in the buffer which can be written.
281: *
282: * @since ostermillerutils 1.00.00
283: */
284: private int spaceLeft() {
285: if (writePosition < markPosition) {
286: // any space between the first write and
287: // the mark except one byte is available.
288: // In this case it is all in one piece.
289: return (markPosition - writePosition - 1);
290: }
291: // space at the beginning and end.
292: return ((buffer.length - 1) - (writePosition - markPosition));
293: }
294:
295: /**
296: * Bytes available for reading.
297: *
298: * @since ostermillerutils 1.00.00
299: */
300: private int available() {
301: if (readPosition <= writePosition) {
302: // any space between the first read and
303: // the first write is available. In this case i
304: // is all in one piece.
305: return (writePosition - readPosition);
306: }
307: // space at the beginning and end.
308: return (buffer.length - (readPosition - writePosition));
309: }
310:
311: /**
312: * Bytes saved for supporting marks.
313: *
314: * @since ostermillerutils 1.00.00
315: */
316: private int marked() {
317: if (markPosition <= readPosition) {
318: // any space between the markPosition and
319: // the first write is marked. In this case i
320: // is all in one piece.
321: return (readPosition - markPosition);
322: }
323: // space at the beginning and end.
324: return (buffer.length - (markPosition - readPosition));
325: }
326:
327: /**
328: * If we have passed the markSize reset the
329: * mark so that the space can be used.
330: *
331: * @since ostermillerutils 1.00.00
332: */
333: private void ensureMark() {
334: if (marked() >= markSize) {
335: markPosition = readPosition;
336: markSize = 0;
337: }
338: }
339:
340: /**
341: * Create a new buffer with a default capacity.
342: * Writing to a full buffer will block until space
343: * is available rather than throw an exception.
344: *
345: * @since ostermillerutils 1.00.00
346: */
347: public CircularByteBuffer() {
348: this (DEFAULT_SIZE, true);
349: }
350:
351: /**
352: * Create a new buffer with given capacity.
353: * Writing to a full buffer will block until space
354: * is available rather than throw an exception.
355: * <p>
356: * Note that the buffer may reserve some bytes for
357: * special purposes and capacity number of bytes may
358: * not be able to be written to the buffer.
359: * <p>
360: * Note that if the buffer is of INFINITE_SIZE it will
361: * neither block or throw exceptions, but rather grow
362: * without bound.
363: *
364: * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
365: *
366: * @since ostermillerutils 1.00.00
367: */
368: public CircularByteBuffer(int size) {
369: this (size, true);
370: }
371:
372: /**
373: * Create a new buffer with a default capacity and
374: * given blocking behavior.
375: *
376: * @param blockingWrite true writing to a full buffer should block
377: * until space is available, false if an exception should
378: * be thrown instead.
379: *
380: * @since ostermillerutils 1.00.00
381: */
382: public CircularByteBuffer(boolean blockingWrite) {
383: this (DEFAULT_SIZE, blockingWrite);
384: }
385:
386: /**
387: * Create a new buffer with the given capacity and
388: * blocking behavior.
389: * <p>
390: * Note that the buffer may reserve some bytes for
391: * special purposes and capacity number of bytes may
392: * not be able to be written to the buffer.
393: * <p>
394: * Note that if the buffer is of INFINITE_SIZE it will
395: * neither block or throw exceptions, but rather grow
396: * without bound.
397: *
398: * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
399: * @param blockingWrite true writing to a full buffer should block
400: * until space is available, false if an exception should
401: * be thrown instead.
402: *
403: * @since ostermillerutils 1.00.00
404: */
405: public CircularByteBuffer(int size, boolean blockingWrite) {
406: if (size == INFINITE_SIZE) {
407: buffer = new byte[DEFAULT_SIZE];
408: infinite = true;
409: } else {
410: buffer = new byte[size];
411: infinite = false;
412: }
413: this .blockingWrite = blockingWrite;
414: }
415:
416: /**
417: * Class for reading from a circular byte buffer.
418: *
419: * @since ostermillerutils 1.00.00
420: */
421: protected class CircularByteBufferInputStream extends InputStream {
422:
423: /**
424: * Returns the number of bytes that can be read (or skipped over) from this
425: * input stream without blocking by the next caller of a method for this input
426: * stream. The next caller might be the same thread or or another thread.
427: *
428: * @return the number of bytes that can be read from this input stream without blocking.
429: * @throws IOException if the stream is closed.
430: *
431: * @since ostermillerutils 1.00.00
432: */
433: @Override
434: public int available() throws IOException {
435: synchronized (CircularByteBuffer.this ) {
436: if (inputStreamClosed)
437: throw new IOException(
438: "InputStream has been closed, it is not ready.");
439: return (CircularByteBuffer.this .available());
440: }
441: }
442:
443: /**
444: * Close the stream. Once a stream has been closed, further read(), available(),
445: * mark(), or reset() invocations will throw an IOException. Closing a
446: * previously-closed stream, however, has no effect.
447: *
448: * @throws IOException never.
449: *
450: * @since ostermillerutils 1.00.00
451: */
452: @Override
453: public void close() throws IOException {
454: synchronized (CircularByteBuffer.this ) {
455: inputStreamClosed = true;
456: }
457: }
458:
459: /**
460: * Mark the present position in the stream. Subsequent calls to reset() will
461: * attempt to reposition the stream to this point.
462: * <p>
463: * The readAheadLimit must be less than the size of circular buffer, otherwise
464: * this method has no effect.
465: *
466: * @param readAheadLimit Limit on the number of bytes that may be read while
467: * still preserving the mark. After reading this many bytes, attempting to
468: * reset the stream will fail.
469: *
470: * @since ostermillerutils 1.00.00
471: */
472: @Override
473: public void mark(int readAheadLimit) {
474: synchronized (CircularByteBuffer.this ) {
475: //if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot mark a closed InputStream.");
476: if (buffer.length - 1 > readAheadLimit) {
477: markSize = readAheadLimit;
478: markPosition = readPosition;
479: }
480: }
481: }
482:
483: /**
484: * Tell whether this stream supports the mark() operation.
485: *
486: * @return true, mark is supported.
487: *
488: * @since ostermillerutils 1.00.00
489: */
490: @Override
491: public boolean markSupported() {
492: return true;
493: }
494:
495: /**
496: * Read a single byte.
497: * This method will block until a byte is available, an I/O error occurs,
498: * or the end of the stream is reached.
499: *
500: * @return The byte read, as an integer in the range 0 to 255 (0x00-0xff),
501: * or -1 if the end of the stream has been reached
502: * @throws IOException if the stream is closed.
503: *
504: * @since ostermillerutils 1.00.00
505: */
506: @Override
507: public int read() throws IOException {
508: while (true) {
509: synchronized (CircularByteBuffer.this ) {
510: if (inputStreamClosed)
511: throw new IOException(
512: "InputStream has been closed; cannot read from a closed InputStream.");
513: int available = CircularByteBuffer.this .available();
514: if (available > 0) {
515: int result = buffer[readPosition] & 0xff;
516: readPosition++;
517: if (readPosition == buffer.length) {
518: readPosition = 0;
519: }
520: ensureMark();
521: return result;
522: } else if (outputStreamClosed) {
523: return -1;
524: }
525: }
526: try {
527: Thread.sleep(100);
528: } catch (Exception x) {
529: throw new IOException(
530: "Blocking read operation interrupted.");
531: }
532: }
533: }
534:
535: /**
536: * Read bytes into an array.
537: * This method will block until some input is available,
538: * an I/O error occurs, or the end of the stream is reached.
539: *
540: * @param cbuf Destination buffer.
541: * @return The number of bytes read, or -1 if the end of
542: * the stream has been reached
543: * @throws IOException if the stream is closed.
544: *
545: * @since ostermillerutils 1.00.00
546: */
547: @Override
548: public int read(byte[] cbuf) throws IOException {
549: return read(cbuf, 0, cbuf.length);
550: }
551:
552: /**
553: * Read bytes into a portion of an array.
554: * This method will block until some input is available,
555: * an I/O error occurs, or the end of the stream is reached.
556: *
557: * @param cbuf Destination buffer.
558: * @param off Offset at which to start storing bytes.
559: * @param len Maximum number of bytes to read.
560: * @return The number of bytes read, or -1 if the end of
561: * the stream has been reached
562: * @throws IOException if the stream is closed.
563: *
564: * @since ostermillerutils 1.00.00
565: */
566: @Override
567: public int read(byte[] cbuf, int off, int len)
568: throws IOException {
569: while (true) {
570: synchronized (CircularByteBuffer.this ) {
571: if (inputStreamClosed)
572: throw new IOException(
573: "InputStream has been closed; cannot read from a closed InputStream.");
574: int available = CircularByteBuffer.this .available();
575: if (available > 0) {
576: int length = Math.min(len, available);
577: int firstLen = Math.min(length, buffer.length
578: - readPosition);
579: int secondLen = length - firstLen;
580: System.arraycopy(buffer, readPosition, cbuf,
581: off, firstLen);
582: if (secondLen > 0) {
583: System.arraycopy(buffer, 0, cbuf, off
584: + firstLen, secondLen);
585: readPosition = secondLen;
586: } else {
587: readPosition += length;
588: }
589: if (readPosition == buffer.length) {
590: readPosition = 0;
591: }
592: ensureMark();
593: return length;
594: } else if (outputStreamClosed) {
595: return -1;
596: }
597: }
598: try {
599: Thread.sleep(100);
600: } catch (Exception x) {
601: throw new IOException(
602: "Blocking read operation interrupted.");
603: }
604: }
605: }
606:
607: /**
608: * Reset the stream.
609: * If the stream has been marked, then attempt to reposition i
610: * at the mark. If the stream has not been marked, or more bytes
611: * than the readAheadLimit have been read, this method has no effect.
612: *
613: * @throws IOException if the stream is closed.
614: *
615: * @since ostermillerutils 1.00.00
616: */
617: @Override
618: public void reset() throws IOException {
619: synchronized (CircularByteBuffer.this ) {
620: if (inputStreamClosed)
621: throw new IOException(
622: "InputStream has been closed; cannot reset a closed InputStream.");
623: readPosition = markPosition;
624: }
625: }
626:
627: /**
628: * Skip bytes.
629: * This method will block until some bytes are available,
630: * an I/O error occurs, or the end of the stream is reached.
631: *
632: * @param n The number of bytes to skip
633: * @return The number of bytes actually skipped
634: * @throws IllegalArgumentException if n is negative.
635: * @throws IOException if the stream is closed.
636: *
637: * @since ostermillerutils 1.00.00
638: */
639: @Override
640: public long skip(long n) throws IOException,
641: IllegalArgumentException {
642: while (true) {
643: synchronized (CircularByteBuffer.this ) {
644: if (inputStreamClosed)
645: throw new IOException(
646: "InputStream has been closed; cannot skip bytes on a closed InputStream.");
647: int available = CircularByteBuffer.this .available();
648: if (available > 0) {
649: int length = Math.min((int) n, available);
650: int firstLen = Math.min(length, buffer.length
651: - readPosition);
652: int secondLen = length - firstLen;
653: if (secondLen > 0) {
654: readPosition = secondLen;
655: } else {
656: readPosition += length;
657: }
658: if (readPosition == buffer.length) {
659: readPosition = 0;
660: }
661: ensureMark();
662: return length;
663: } else if (outputStreamClosed) {
664: return 0;
665: }
666: }
667: try {
668: Thread.sleep(100);
669: } catch (Exception x) {
670: throw new IOException(
671: "Blocking read operation interrupted.");
672: }
673: }
674: }
675: }
676:
677: /**
678: * Class for writing to a circular byte buffer.
679: * If the buffer is full, the writes will either block
680: * until there is some space available or throw an IOException
681: * based on the CircularByteBuffer's preference.
682: *
683: * @since ostermillerutils 1.00.00
684: */
685: protected class CircularByteBufferOutputStream extends OutputStream {
686:
687: /**
688: * Close the stream, flushing it first.
689: * This will cause the InputStream associated with this circular buffer
690: * to read its last bytes once it empties the buffer.
691: * Once a stream has been closed, further write() or flush() invocations
692: * will cause an IOException to be thrown. Closing a previously-closed stream,
693: * however, has no effect.
694: *
695: * @throws IOException never.
696: *
697: * @since ostermillerutils 1.00.00
698: */
699: @Override
700: public void close() throws IOException {
701: synchronized (CircularByteBuffer.this ) {
702: if (!outputStreamClosed) {
703: flush();
704: }
705: outputStreamClosed = true;
706: }
707: }
708:
709: /**
710: * Flush the stream.
711: *
712: * @throws IOException if the stream is closed.
713: *
714: * @since ostermillerutils 1.00.00
715: */
716: @Override
717: public void flush() throws IOException {
718: if (outputStreamClosed)
719: throw new IOException(
720: "OutputStream has been closed; cannot flush a closed OutputStream.");
721: if (inputStreamClosed)
722: throw new IOException(
723: "Buffer closed by inputStream; cannot flush.");
724: // this method needs to do nothing
725: }
726:
727: /**
728: * Write an array of bytes.
729: * If the buffer allows blocking writes, this method will block until
730: * all the data has been written rather than throw an IOException.
731: *
732: * @param cbuf Array of bytes to be written
733: * @throws BufferOverflowException if buffer does not allow blocking writes
734: * and the buffer is full. If the exception is thrown, no data
735: * will have been written since the buffer was set to be non-blocking.
736: * @throws IOException if the stream is closed, or the write is interrupted.
737: *
738: * @since ostermillerutils 1.00.00
739: */
740: @Override
741: public void write(byte[] cbuf) throws IOException {
742: write(cbuf, 0, cbuf.length);
743: }
744:
745: /**
746: * Write a portion of an array of bytes.
747: * If the buffer allows blocking writes, this method will block until
748: * all the data has been written rather than throw an IOException.
749: *
750: * @param cbuf Array of bytes
751: * @param off Offset from which to start writing bytes
752: * @param len - Number of bytes to write
753: * @throws BufferOverflowException if buffer does not allow blocking writes
754: * and the buffer is full. If the exception is thrown, no data
755: * will have been written since the buffer was set to be non-blocking.
756: * @throws IOException if the stream is closed, or the write is interrupted.
757: *
758: * @since ostermillerutils 1.00.00
759: */
760: @Override
761: public void write(byte[] cbuf, int off, int len)
762: throws IOException {
763: while (len > 0) {
764: synchronized (CircularByteBuffer.this ) {
765: if (outputStreamClosed)
766: throw new IOException(
767: "OutputStream has been closed; cannot write to a closed OutputStream.");
768: if (inputStreamClosed)
769: throw new IOException(
770: "Buffer closed by InputStream; cannot write to a closed buffer.");
771: int spaceLeft = spaceLeft();
772: while (infinite && spaceLeft < len) {
773: resize();
774: spaceLeft = spaceLeft();
775: }
776: if (!blockingWrite && spaceLeft < len)
777: throw new BufferOverflowException(
778: "CircularByteBuffer is full; cannot write "
779: + len + " bytes");
780: int realLen = Math.min(len, spaceLeft);
781: int firstLen = Math.min(realLen, buffer.length
782: - writePosition);
783: int secondLen = Math.min(realLen - firstLen,
784: buffer.length - markPosition - 1);
785: int written = firstLen + secondLen;
786: if (firstLen > 0) {
787: System.arraycopy(cbuf, off, buffer,
788: writePosition, firstLen);
789: }
790: if (secondLen > 0) {
791: System.arraycopy(cbuf, off + firstLen, buffer,
792: 0, secondLen);
793: writePosition = secondLen;
794: } else {
795: writePosition += written;
796: }
797: if (writePosition == buffer.length) {
798: writePosition = 0;
799: }
800: off += written;
801: len -= written;
802: }
803: if (len > 0) {
804: try {
805: Thread.sleep(100);
806: } catch (Exception x) {
807: throw new IOException(
808: "Waiting for available space in buffer interrupted.");
809: }
810: }
811: }
812: }
813:
814: /**
815: * Write a single byte.
816: * The byte to be written is contained in the 8 low-order bits of the
817: * given integer value; the 24 high-order bits are ignored.
818: * If the buffer allows blocking writes, this method will block until
819: * all the data has been written rather than throw an IOException.
820: *
821: * @param c number of bytes to be written
822: * @throws BufferOverflowException if buffer does not allow blocking writes
823: * and the buffer is full.
824: * @throws IOException if the stream is closed, or the write is interrupted.
825: *
826: * @since ostermillerutils 1.00.00
827: */
828: @Override
829: public void write(int c) throws IOException {
830: boolean written = false;
831: while (!written) {
832: synchronized (CircularByteBuffer.this ) {
833: if (outputStreamClosed)
834: throw new IOException(
835: "OutputStream has been closed; cannot write to a closed OutputStream.");
836: if (inputStreamClosed)
837: throw new IOException(
838: "Buffer closed by InputStream; cannot write to a closed buffer.");
839: int spaceLeft = spaceLeft();
840: while (infinite && spaceLeft < 1) {
841: resize();
842: spaceLeft = spaceLeft();
843: }
844: if (!blockingWrite && spaceLeft < 1)
845: throw new BufferOverflowException(
846: "CircularByteBuffer is full; cannot write 1 byte");
847: if (spaceLeft > 0) {
848: buffer[writePosition] = (byte) (c & 0xff);
849: writePosition++;
850: if (writePosition == buffer.length) {
851: writePosition = 0;
852: }
853: written = true;
854: }
855: }
856: if (!written) {
857: try {
858: Thread.sleep(100);
859: } catch (Exception x) {
860: throw new IOException(
861: "Waiting for available space in buffer interrupted.");
862: }
863: }
864: }
865: }
866: }
867: }
|