KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Ostermiller > util > CircularByteBuffer


1 /*
2  * Circular Byte Buffer
3  * Copyright (C) 2002 Stephen Ostermiller
4  * http://ostermiller.org/contact.pl?regarding=Java+Utilities
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * See COPYING.TXT for details.
17  */

18 package com.Ostermiller.util;
19
20 import java.io.InputStream JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23
24 /**
25  * Implements the Circular Buffer producer/consumer model for bytes.
26  * More information about this class is available from <a target="_top" HREF=
27  * "http://ostermiller.org/utils/CircularByteBuffer.html">ostermiller.org</a>.
28  * <p>
29  * Using this class is a simpler alternative to using a PipedInputStream
30  * and a PipedOutputStream. PipedInputStreams and PipedOutputStreams don't support the
31  * mark operation, don't allow you to control buffer sizes that they use,
32  * and have a more complicated API that requires a instantiating two
33  * classes and connecting them.
34  * <p>
35  * This class is thread safe.
36  *
37  * @see CircularCharBuffer
38  * @see CircularObjectBuffer
39  *
40  * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
41  * @since ostermillerutils 1.00.00
42  */

43 public class CircularByteBuffer {
44
45     /**
46      * The default size for a circular byte buffer.
47      *
48      * @since ostermillerutils 1.00.00
49      */

50     private final static int DEFAULT_SIZE = 1024;
51
52     /**
53      * A buffer that will grow as things are added.
54      *
55      * @since ostermillerutils 1.00.00
56      */

57     public final static int INFINITE_SIZE = -1;
58
59     /**
60      * The circular buffer.
61      * <p>
62      * The actual capacity of the buffer is one less than the actual length
63      * of the buffer so that an empty and a full buffer can be
64      * distinguished. An empty buffer will have the markPostion and the
65      * writePosition equal to each other. A full buffer will have
66      * the writePosition one less than the markPostion.
67      * <p>
68      * There are three important indexes into the buffer:
69      * The readPosition, the writePosition, and the markPosition.
70      * If the InputStream has never been marked, the readPosition and
71      * the markPosition should always be the same. The bytes
72      * available to be read go from the readPosition to the writePosition,
73      * wrapping around the end of the buffer. The space available for writing
74      * goes from the write position to one less than the markPosition,
75      * wrapping around the end of the buffer. The bytes that have
76      * been saved to support a reset() of the InputStream go from markPosition
77      * to readPosition, wrapping around the end of the buffer.
78      *
79      * @since ostermillerutils 1.00.00
80      */

81     protected byte[] buffer;
82     /**
83      * Index of the first byte available to be read.
84      *
85      * @since ostermillerutils 1.00.00
86      */

87     protected volatile int readPosition = 0;
88     /**
89      * Index of the first byte available to be written.
90      *
91      * @since ostermillerutils 1.00.00
92      */

93     protected volatile int writePosition = 0;
94     /**
95      * Index of the first saved byte. (To support stream marking.)
96      *
97      * @since ostermillerutils 1.00.00
98      */

99     protected volatile int markPosition = 0;
100     /**
101      * Number of bytes that have to be saved
102      * to support mark() and reset() on the InputStream.
103      *
104      * @since ostermillerutils 1.00.00
105      */

106     protected volatile int markSize = 0;
107     /**
108      * If this buffer is infinite (should resize itself when full)
109      *
110      * @since ostermillerutils 1.00.00
111      */

112     protected volatile boolean infinite = false;
113     /**
114      * True if a write to a full buffer should block until the buffer
115      * has room, false if the write method should throw an IOException
116      *
117      * @since ostermillerutils 1.00.00
118      */

119     protected boolean blockingWrite = true;
120     /**
121      * The InputStream that can empty this buffer.
122      *
123      * @since ostermillerutils 1.00.00
124      */

125     protected InputStream JavaDoc in = new CircularByteBufferInputStream();
126     /**
127      * true if the close() method has been called on the InputStream
128      *
129      * @since ostermillerutils 1.00.00
130      */

131     protected boolean inputStreamClosed = false;
132     /**
133      * The OutputStream that can fill this buffer.
134      *
135      * @since ostermillerutils 1.00.00
136      */

137     protected OutputStream JavaDoc out = new CircularByteBufferOutputStream();
138     /**
139      * true if the close() method has been called on the OutputStream
140      *
141      * @since ostermillerutils 1.00.00
142      */

143     protected boolean outputStreamClosed = false;
144
145     /**
146      * Make this buffer ready for reuse. The contents of the buffer
147      * will be cleared and the streams associated with this buffer
148      * will be reopened if they had been closed.
149      *
150      * @since ostermillerutils 1.00.00
151      */

152     public void clear(){
153         synchronized (this){
154             readPosition = 0;
155             writePosition = 0;
156             markPosition = 0;
157             outputStreamClosed = false;
158             inputStreamClosed = false;
159         }
160     }
161
162     /**
163      * Retrieve a OutputStream that can be used to fill
164      * this buffer.
165      * <p>
166      * Write methods may throw a BufferOverflowException if
167      * the buffer is not large enough. A large enough buffer
168      * size must be chosen so that this does not happen or
169      * the caller must be prepared to catch the exception and
170      * try again once part of the buffer has been consumed.
171      *
172      *
173      * @return the producer for this buffer.
174      *
175      * @since ostermillerutils 1.00.00
176      */

177     public OutputStream JavaDoc getOutputStream(){
178         return out;
179     }
180
181     /**
182      * Retrieve a InputStream that can be used to empty
183      * this buffer.
184      * <p>
185      * This InputStream supports marks at the expense
186      * of the buffer size.
187      *
188      * @return the consumer for this buffer.
189      *
190      * @since ostermillerutils 1.00.00
191      */

192     public InputStream JavaDoc getInputStream(){
193         return in;
194     }
195
196     /**
197      * Get number of bytes that are available to be read.
198      * <p>
199      * Note that the number of bytes available plus
200      * the number of bytes free may not add up to the
201      * capacity of this buffer, as the buffer may reserve some
202      * space for other purposes.
203      *
204      * @return the size in bytes of this buffer
205      *
206      * @since ostermillerutils 1.00.00
207      */

208     public int getAvailable(){
209         synchronized (this){
210             return available();
211         }
212     }
213
214     /**
215      * Get the number of bytes this buffer has free for
216      * writing.
217      * <p>
218      * Note that the number of bytes available plus
219      * the number of bytes free may not add up to the
220      * capacity of this buffer, as the buffer may reserve some
221      * space for other purposes.
222      *
223      * @return the available space in bytes of this buffer
224      *
225      * @since ostermillerutils 1.00.00
226      */

227     public int getSpaceLeft(){
228         synchronized (this){
229             return spaceLeft();
230         }
231     }
232
233     /**
234      * Get the capacity of this buffer.
235      * <p>
236      * Note that the number of bytes available plus
237      * the number of bytes free may not add up to the
238      * capacity of this buffer, as the buffer may reserve some
239      * space for other purposes.
240      *
241      * @return the size in bytes of this buffer
242      *
243      * @since ostermillerutils 1.00.00
244      */

245     public int getSize(){
246         synchronized (this){
247             return buffer.length;
248         }
249     }
250
251     /**
252      * double the size of the buffer
253      *
254      * @since ostermillerutils 1.00.00
255      */

256     private void resize(){
257         byte[] newBuffer = new byte[buffer.length * 2];
258         int marked = marked();
259         int available = available();
260         if (markPosition <= writePosition){
261             // any space between the mark and
262
// the first write needs to be saved.
263
// In this case it is all in one piece.
264
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     /**
279      * Space available in the buffer which can be written.
280      *
281      * @since ostermillerutils 1.00.00
282      */

283     private int spaceLeft(){
284         if (writePosition < markPosition){
285             // any space between the first write and
286
// the mark except one byte is available.
287
// In this case it is all in one piece.
288
return (markPosition - writePosition - 1);
289         } else {
290             // space at the beginning and end.
291
return ((buffer.length - 1) - (writePosition - markPosition));
292         }
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         } else {
307             // space at the beginning and end.
308
return (buffer.length - (readPosition - writePosition));
309         }
310     }
311
312     /**
313      * Bytes saved for supporting marks.
314      *
315      * @since ostermillerutils 1.00.00
316      */

317     private int marked(){
318         if (markPosition <= readPosition){
319             // any space between the markPosition and
320
// the first write is marked. In this case i
321
// is all in one piece.
322
return (readPosition - markPosition);
323         } else {
324             // space at the beginning and end.
325
return (buffer.length - (markPosition - readPosition));
326         }
327     }
328
329     /**
330      * If we have passed the markSize reset the
331      * mark so that the space can be used.
332      *
333      * @since ostermillerutils 1.00.00
334      */

335     private void ensureMark(){
336         if (marked() >= markSize){
337             markPosition = readPosition;
338             markSize = 0;
339         }
340     }
341
342     /**
343      * Create a new buffer with a default capacity.
344      * Writing to a full buffer will block until space
345      * is available rather than throw an exception.
346      *
347      * @since ostermillerutils 1.00.00
348      */

349     public CircularByteBuffer(){
350         this (DEFAULT_SIZE, true);
351     }
352
353     /**
354      * Create a new buffer with given capacity.
355      * Writing to a full buffer will block until space
356      * is available rather than throw an exception.
357      * <p>
358      * Note that the buffer may reserve some bytes for
359      * special purposes and capacity number of bytes may
360      * not be able to be written to the buffer.
361      * <p>
362      * Note that if the buffer is of INFINITE_SIZE it will
363      * neither block or throw exceptions, but rather grow
364      * without bound.
365      *
366      * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
367      *
368      * @since ostermillerutils 1.00.00
369      */

370     public CircularByteBuffer(int size){
371         this (size, true);
372     }
373
374     /**
375      * Create a new buffer with a default capacity and
376      * given blocking behavior.
377      *
378      * @param blockingWrite true writing to a full buffer should block
379      * until space is available, false if an exception should
380      * be thrown instead.
381      *
382      * @since ostermillerutils 1.00.00
383      */

384     public CircularByteBuffer(boolean blockingWrite){
385         this (DEFAULT_SIZE, blockingWrite);
386     }
387
388     /**
389      * Create a new buffer with the given capacity and
390      * blocking behavior.
391      * <p>
392      * Note that the buffer may reserve some bytes for
393      * special purposes and capacity number of bytes may
394      * not be able to be written to the buffer.
395      * <p>
396      * Note that if the buffer is of INFINITE_SIZE it will
397      * neither block or throw exceptions, but rather grow
398      * without bound.
399      *
400      * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
401      * @param blockingWrite true writing to a full buffer should block
402      * until space is available, false if an exception should
403      * be thrown instead.
404      *
405      * @since ostermillerutils 1.00.00
406      */

407     public CircularByteBuffer(int size, boolean blockingWrite){
408         if (size == INFINITE_SIZE){
409             buffer = new byte[DEFAULT_SIZE];
410             infinite = true;
411         } else {
412             buffer = new byte[size];
413             infinite = false;
414         }
415         this.blockingWrite = blockingWrite;
416     }
417
418     /**
419      * Class for reading from a circular byte buffer.
420      *
421      * @since ostermillerutils 1.00.00
422      */

423     protected class CircularByteBufferInputStream extends InputStream JavaDoc {
424
425         /**
426          * Returns the number of bytes that can be read (or skipped over) from this
427          * input stream without blocking by the next caller of a method for this input
428          * stream. The next caller might be the same thread or or another thread.
429          *
430          * @return the number of bytes that can be read from this input stream without blocking.
431          * @throws IOException if the stream is closed.
432          *
433          * @since ostermillerutils 1.00.00
434          */

435         public int available() throws IOException JavaDoc {
436             synchronized (CircularByteBuffer.this){
437                 if (inputStreamClosed) throw new IOException JavaDoc("InputStream has been closed, it is not ready.");
438                 return (CircularByteBuffer.this.available());
439             }
440         }
441
442         /**
443          * Close the stream. Once a stream has been closed, further read(), available(),
444          * mark(), or reset() invocations will throw an IOException. Closing a
445          * previously-closed stream, however, has no effect.
446          *
447          * @throws IOException never.
448          *
449          * @since ostermillerutils 1.00.00
450          */

451         public void close() throws IOException JavaDoc {
452             synchronized (CircularByteBuffer.this){
453                 inputStreamClosed = true;
454             }
455         }
456
457         /**
458          * Mark the present position in the stream. Subsequent calls to reset() will
459          * attempt to reposition the stream to this point.
460          * <p>
461          * The readAheadLimit must be less than the size of circular buffer, otherwise
462          * this method has no effect.
463          *
464          * @param readAheadLimit Limit on the number of bytes that may be read while
465          * still preserving the mark. After reading this many bytes, attempting to
466          * reset the stream will fail.
467          *
468          * @since ostermillerutils 1.00.00
469          */

470         public void mark(int readAheadLimit) {
471             synchronized (CircularByteBuffer.this){
472                 //if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot mark a closed InputStream.");
473
if (buffer.length - 1 > readAheadLimit) {
474                     markSize = readAheadLimit;
475                     markPosition = readPosition;
476                 }
477             }
478         }
479
480         /**
481          * Tell whether this stream supports the mark() operation.
482          *
483          * @return true, mark is supported.
484          *
485          * @since ostermillerutils 1.00.00
486          */

487         public boolean markSupported() {
488             return true;
489         }
490
491         /**
492          * Read a single byte.
493          * This method will block until a byte is available, an I/O error occurs,
494          * or the end of the stream is reached.
495          *
496          * @return The byte read, as an integer in the range 0 to 255 (0x00-0xff),
497          * or -1 if the end of the stream has been reached
498          * @throws IOException if the stream is closed.
499          *
500          * @since ostermillerutils 1.00.00
501          */

502         public int read() throws IOException JavaDoc {
503             while (true){
504                 synchronized (CircularByteBuffer.this){
505                     if (inputStreamClosed) throw new IOException JavaDoc("InputStream has been closed; cannot read from a closed InputStream.");
506                     int available = CircularByteBuffer.this.available();
507                     if (available > 0){
508                         int result = buffer[readPosition] & 0xff;
509                         readPosition++;
510                         if (readPosition == buffer.length){
511                             readPosition = 0;
512                         }
513                         ensureMark();
514                         return result;
515                     } else if (outputStreamClosed){
516                         return -1;
517                     }
518                 }
519                 try {
520                     Thread.sleep(100);
521                 } catch(Exception JavaDoc x){
522                     throw new IOException JavaDoc("Blocking read operation interrupted.");
523                 }
524             }
525         }
526
527         /**
528          * Read bytes into an array.
529          * This method will block until some input is available,
530          * an I/O error occurs, or the end of the stream is reached.
531          *
532          * @param cbuf Destination buffer.
533          * @return The number of bytes read, or -1 if the end of
534          * the stream has been reached
535          * @throws IOException if the stream is closed.
536          *
537          * @since ostermillerutils 1.00.00
538          */

539         public int read(byte[] cbuf) throws IOException JavaDoc {
540             return read(cbuf, 0, cbuf.length);
541         }
542
543         /**
544          * Read bytes into a portion of an array.
545          * This method will block until some input is available,
546          * an I/O error occurs, or the end of the stream is reached.
547          *
548          * @param cbuf Destination buffer.
549          * @param off Offset at which to start storing bytes.
550          * @param len Maximum number of bytes to read.
551          * @return The number of bytes read, or -1 if the end of
552          * the stream has been reached
553          * @throws IOException if the stream is closed.
554          *
555          * @since ostermillerutils 1.00.00
556          */

557         public int read(byte[] cbuf, int off, int len) throws IOException JavaDoc {
558             while (true){
559                 synchronized (CircularByteBuffer.this){
560                     if (inputStreamClosed) throw new IOException JavaDoc("InputStream has been closed; cannot read from a closed InputStream.");
561                     int available = CircularByteBuffer.this.available();
562                     if (available > 0){
563                         int length = Math.min(len, available);
564                         int firstLen = Math.min(length, buffer.length - readPosition);
565                         int secondLen = length - firstLen;
566                         System.arraycopy(buffer, readPosition, cbuf, off, firstLen);
567                         if (secondLen > 0){
568                             System.arraycopy(buffer, 0, cbuf, off+firstLen, secondLen);
569                             readPosition = secondLen;
570                         } else {
571                             readPosition += length;
572                         }
573                         if (readPosition == buffer.length) {
574                             readPosition = 0;
575                         }
576                         ensureMark();
577                         return length;
578                     } else if (outputStreamClosed){
579                         return -1;
580                     }
581                 }
582                 try {
583                     Thread.sleep(100);
584                 } catch(Exception JavaDoc x){
585                     throw new IOException JavaDoc("Blocking read operation interrupted.");
586                 }
587             }
588         }
589
590         /**
591          * Reset the stream.
592          * If the stream has been marked, then attempt to reposition i
593          * at the mark. If the stream has not been marked, or more bytes
594          * than the readAheadLimit have been read, this method has no effect.
595          *
596          * @throws IOException if the stream is closed.
597          *
598          * @since ostermillerutils 1.00.00
599          */

600         public void reset() throws IOException JavaDoc {
601             synchronized (CircularByteBuffer.this){
602                 if (inputStreamClosed) throw new IOException JavaDoc("InputStream has been closed; cannot reset a closed InputStream.");
603                 readPosition = markPosition;
604             }
605         }
606
607         /**
608          * Skip bytes.
609          * This method will block until some bytes are available,
610          * an I/O error occurs, or the end of the stream is reached.
611          *
612          * @param n The number of bytes to skip
613          * @return The number of bytes actually skipped
614          * @throws IllegalArgumentException if n is negative.
615          * @throws IOException if the stream is closed.
616          *
617          * @since ostermillerutils 1.00.00
618          */

619         public long skip(long n) throws IOException JavaDoc, IllegalArgumentException JavaDoc {
620             while (true){
621                 synchronized (CircularByteBuffer.this){
622                     if (inputStreamClosed) throw new IOException JavaDoc("InputStream has been closed; cannot skip bytes on a closed InputStream.");
623                     int available = CircularByteBuffer.this.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 (outputStreamClosed){
638                         return 0;
639                     }
640                 }
641                 try {
642                     Thread.sleep(100);
643                 } catch(Exception JavaDoc x){
644                     throw new IOException JavaDoc("Blocking read operation interrupted.");
645                 }
646             }
647         }
648     }
649
650     /**
651      * Class for writing to a circular byte buffer.
652      * If the buffer is full, the writes will either block
653      * until there is some space available or throw an IOException
654      * based on the CircularByteBuffer's preference.
655      *
656      * @since ostermillerutils 1.00.00
657      */

658     protected class CircularByteBufferOutputStream extends OutputStream JavaDoc {
659
660         /**
661          * Close the stream, flushing it first.
662          * This will cause the InputStream associated with this circular buffer
663          * to read its last bytes once it empties the buffer.
664          * Once a stream has been closed, further write() or flush() invocations
665          * will cause an IOException to be thrown. Closing a previously-closed stream,
666          * however, has no effect.
667          *
668          * @throws IOException never.
669          *
670          * @since ostermillerutils 1.00.00
671          */

672         public void close() throws IOException JavaDoc {
673             synchronized (CircularByteBuffer.this){
674                 if (!outputStreamClosed){
675                     flush();
676                 }
677                 outputStreamClosed = true;
678             }
679         }
680
681         /**
682          * Flush the stream.
683          *
684          * @throws IOException if the stream is closed.
685          *
686          * @since ostermillerutils 1.00.00
687          */

688         public void flush() throws IOException JavaDoc {
689             if (outputStreamClosed) throw new IOException JavaDoc("OutputStream has been closed; cannot flush a closed OutputStream.");
690             if (inputStreamClosed) throw new IOException JavaDoc("Buffer closed by inputStream; cannot flush.");
691             // this method needs to do nothing
692
}
693
694         /**
695          * Write an array of bytes.
696          * If the buffer allows blocking writes, this method will block until
697          * all the data has been written rather than throw an IOException.
698          *
699          * @param cbuf Array of bytes to be written
700          * @throws BufferOverflowException if buffer does not allow blocking writes
701          * and the buffer is full. If the exception is thrown, no data
702          * will have been written since the buffer was set to be non-blocking.
703          * @throws IOException if the stream is closed, or the write is interrupted.
704          *
705          * @since ostermillerutils 1.00.00
706          */

707         public void write(byte[] cbuf) throws IOException JavaDoc {
708             write(cbuf, 0, cbuf.length);
709         }
710
711         /**
712          * Write a portion of an array of bytes.
713          * If the buffer allows blocking writes, this method will block until
714          * all the data has been written rather than throw an IOException.
715          *
716          * @param cbuf Array of bytes
717          * @param off Offset from which to start writing bytes
718          * @param len - Number of bytes to write
719          * @throws BufferOverflowException if buffer does not allow blocking writes
720          * and the buffer is full. If the exception is thrown, no data
721          * will have been written since the buffer was set to be non-blocking.
722          * @throws IOException if the stream is closed, or the write is interrupted.
723          *
724          * @since ostermillerutils 1.00.00
725          */

726         public void write(byte[] cbuf, int off, int len) throws IOException JavaDoc {
727             while (len > 0){
728                 synchronized (CircularByteBuffer.this){
729                     if (outputStreamClosed) throw new IOException JavaDoc("OutputStream has been closed; cannot write to a closed OutputStream.");
730                     if (inputStreamClosed) throw new IOException JavaDoc("Buffer closed by InputStream; 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("CircularByteBuffer is full; cannot write " + len + " bytes");
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 JavaDoc x){
760                         throw new IOException JavaDoc("Waiting for available space in buffer interrupted.");
761                     }
762                 }
763             }
764         }
765
766         /**
767          * Write a single byte.
768          * The byte to be written is contained in the 8 low-order bits of the
769          * given integer value; the 24 high-order bits are ignored.
770          * If the buffer allows blocking writes, this method will block until
771          * all the data has been written rather than throw an IOException.
772          *
773          * @param c int specifying a byte to be written.
774          * @throws BufferOverflowException if buffer does not allow blocking writes
775          * and the buffer is full.
776          * @throws IOException if the stream is closed, or the write is interrupted.
777          *
778          * @since ostermillerutils 1.00.00
779          */

780         public void write(int c) throws IOException JavaDoc {
781             boolean written = false;
782             while (!written){
783                 synchronized (CircularByteBuffer.this){
784                     if (outputStreamClosed) throw new IOException JavaDoc("OutputStream has been closed; cannot write to a closed OutputStream.");
785                     if (inputStreamClosed) throw new IOException JavaDoc("Buffer closed by InputStream; 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("CircularByteBuffer is full; cannot write 1 byte");
792                     if (spaceLeft > 0){
793                         buffer[writePosition] = (byte)(c & 0xff);
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 JavaDoc x){
805                         throw new IOException JavaDoc("Waiting for available space in buffer interrupted.");
806                     }
807                 }
808             }
809         }
810     }
811 }
812
Popular Tags