jaulib v1.3.0
Jau Support Library (C++, Java, ..)
Bitstream.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2014 Gothel Software e.K.
5 * Copyright (c) 2014 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26package org.jau.io;
27
28import java.io.BufferedInputStream;
29import java.io.BufferedOutputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.nio.ByteBuffer;
34import java.util.Locale;
35
36import org.jau.sys.Debug;
37
38/**
39 * Versatile Bitstream implementation supporting:
40 * <ul>
41 * <li>Utilize I/O operations on I/O streams, buffers and arrays</li>
42 * <li>Consider MSBfirst / LSBfirst mode</li>
43 * <li>Linear bit R/W operations</li>
44 * <li>Bulk R/W operations w/ endian related type conversion</li>
45 * <li>Allow mark/reset and switching streams and input/output mode</li>
46 * <li>Optimized operations</li>
47 * </ul>
48 */
49public class Bitstream<T> {
50 private static final boolean DEBUG = Debug.debug("Bitstream");
51
52 /** End of stream marker, {@value} or 0xFFFFFFFF */
53 public static final int EOS = -1;
54
55 /**
56 * General byte stream.
57 */
58 public static interface ByteStream<T> {
59 /** Sets the underlying stream, without {@link #close()}ing the previous one. */
60 void setStream(final T stream);
61
62 /** Returns the underlying stream */
64
65 /**
66 * Closing the underlying stream, implies {@link #flush()}.
67 * <p>
68 * Implementation will <code>null</code> the stream references,
69 * hence {@link #setStream(Object)} must be called before re-using instance.
70 * </p>
71 * @throws IOException
72 */
73 void close() throws IOException;
74
75 /**
76 * Synchronizes all underlying {@link #canOutput() output stream} operations, or do nothing.
77 * @throws IOException
78 */
79 void flush() throws IOException;
80
81 /** Return true if stream can handle input, i.e. {@link #read()}. */
82 boolean canInput();
83
84 /** Return true if stream can handle output, i.e. {@link #write(byte)} */
85 boolean canOutput();
86
87 /**
88 * Returns the byte position in the stream.
89 */
90 long position();
91
92 /**
93 * Sets this stream's position.
94 * <p>
95 * A set mark is cleared if &gt; new position.
96 * </p>
97 * <p>
98 * Returns {@link Bitstream#EOS} is end-of-stream is reached,
99 * otherwise the new position.
100 * </p>
101 * <p>
102 * Known supporting implementation is {@link ByteBufferStream} and {@link ByteArrayStream}.
103 * </p>
104 *
105 * @param newPosition The new positive position.
106 *
107 * @return The new set position or {@link Bitstream#EOS} if end-of-stream is reached.
108 *
109 * @throws UnsupportedOperationException if not supported, i.e. {@link ByteInputStream} or {@link ByteOutputStream}
110 * @throws IllegalArgumentException If the {@code newPosition} is negative
111 */
112 long position(long newPosition) throws UnsupportedOperationException, IllegalArgumentException;
113
114 /**
115 * It is implementation dependent, whether backward skip giving a negative number is supported or not.
116 * @param n number of bytes to skip
117 * @return actual skipped bytes
118 * @throws IOException
119 */
120 long skip(final long n) throws IOException;
121
122 /**
123 * Set {@code markpos} to current position, allowing the stream to be {@link #reset()}.
124 * @param readlimit maximum number of bytes able to read before invalidating the {@code markpos}.
125 * @throws UnsupportedOperationException if not supported, i.e. if stream is not an {@link #canInput() input stream}.
126 */
127 void mark(final int readLimit) throws UnsupportedOperationException;
128
129 /**
130 * Reset stream position to <i>markpos</i> as set via {@link #mark(int)}.
131 * <p>
132 * <i>markpos</i> is kept, hence {@link #reset()} can be called multiple times.
133 * </p>
134 * @throws UnsupportedOperationException if not supported, i.e. if stream is not an {@link #canInput() input stream}.
135 * @throws IllegalStateException if <i>markpos</i> has not been set via {@link #mark(int)} or reset operation failed.
136 * @throws IOException if reset operation failed.
137 */
138 void reset() throws UnsupportedOperationException, IllegalStateException, IOException;
139
140 /**
141 * Reads one byte from the stream.
142 * <p>
143 * Returns {@link Bitstream#EOS} is end-of-stream is reached,
144 * otherwise the resulting value.
145 * </p>
146 * @throws IOException
147 * @throws UnsupportedOperationException if not supported, i.e. if stream is not an {@link #canInput() input stream}.
148 */
149 int read() throws UnsupportedOperationException, IOException;
150
151 /**
152 * Writes one byte, to the stream.
153 * <p>
154 * Returns {@link Bitstream#EOS} is end-of-stream is reached,
155 * otherwise the written value.
156 * </p>
157 * @throws IOException
158 * @throws UnsupportedOperationException if not supported, i.e. if stream is not an {@link #canOutput() output stream}.
159 */
160 int write(final byte val) throws UnsupportedOperationException, IOException;
161 }
162
163 /**
164 * Specific {@link ByteStream byte stream}.
165 * <p>
166 * Can handle {@link #canInput() input} and {@link #canOutput() output} operations.
167 * </p>
168 */
169 public static class ByteArrayStream implements ByteStream<byte[]> {
170 private byte[] media;
171 private int pos;
172 private int posMark;
173
174 public ByteArrayStream(final byte[] stream) {
175 setStream(stream);
176 }
177
178 @Override
179 public void setStream(final byte[] stream) {
180 media = stream;
181 pos = 0;
182 posMark = -1;
183 }
184
185 @Override
186 public byte[] getStream() { return media; }
187
188 @Override
189 public void close() {
190 media = null;
191 }
192 @Override
193 public void flush() {
194 // NOP
195 }
196
197 @Override
198 public boolean canInput() { return true; }
199
200 @Override
201 public boolean canOutput() { return true; }
202
203 @Override
204 public long position() { return pos; }
205
206 @Override
207 public long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException {
208 if( newPosition >= media.length ) {
209 return Bitstream.EOS;
210 }
211 pos = (int)newPosition;
212 if( posMark > pos ) {
213 posMark = -1;
214 }
215 return pos;
216 }
217
218 @Override
219 public long skip(final long n) {
220 final long skip;
221 if( n >= 0 ) {
222 final int remaining = media.length - pos;
223 skip = Math.min(remaining, (int)n);
224 } else {
225 final int n2 = (int)n * -1;
226 skip = -1 * Math.min(pos, n2);
227 }
228 pos += skip;
229 return skip;
230 }
231
232 @Override
233 public void mark(final int readLimit) {
234 posMark = pos;
235 }
236
237 @Override
238 public void reset() throws IllegalStateException {
239 if( 0 > posMark ) {
240 throw new IllegalStateException("markpos not set");
241 }
242 if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); }
243 pos = posMark;
244 }
245
246 @Override
247 public int read() {
248 final int r;
249 if( media.length > pos ) {
250 r = 0xff & media[pos++];
251 } else {
252 r = -1; // EOS
253 }
254 if( DEBUG ) {
255 if( EOS != r ) {
256 System.err.println("u8["+(pos-1)+"] -> "+toHexBinString(true, r, 8));
257 } else {
258 System.err.println("u8["+(pos-0)+"] -> EOS");
259 }
260 }
261 return r;
262 }
263
264 @Override
265 public int write(final byte val) {
266 final int r;
267 if( media.length > pos ) {
268 media[pos++] = val;
269 r = 0xff & val;
270 } else {
271 r = -1; // EOS
272 }
273 if( DEBUG ) {
274 if( EOS != r ) {
275 System.err.println("u8["+(pos-1)+"] <- "+toHexBinString(true, r, 8));
276 } else {
277 System.err.println("u8["+(pos-0)+"] <- EOS");
278 }
279 }
280 return r;
281 }
282 }
283
284 /**
285 * Specific {@link ByteStream byte stream}.
286 * <p>
287 * Can handle {@link #canInput() input} and {@link #canOutput() output} operations.
288 * </p>
289 */
290 public static class ByteBufferStream implements ByteStream<ByteBuffer> {
291 private ByteBuffer media;
292 private int pos;
293 private int posMark;
294
295 public ByteBufferStream(final ByteBuffer stream) {
296 setStream(stream);
297 }
298
299 @Override
300 public void setStream(final ByteBuffer stream) {
301 media = stream;
302 pos = 0;
303 posMark = -1;
304 }
305
306 @Override
307 public ByteBuffer getStream() { return media; }
308
309 @Override
310 public void close() {
311 media = null;
312 }
313 @Override
314 public void flush() {
315 // NOP
316 }
317
318 @Override
319 public boolean canInput() { return true; }
320
321 @Override
322 public boolean canOutput() { return true; }
323
324 @Override
325 public long position() { return pos; }
326
327 @Override
328 public long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException {
329 if( newPosition >= media.limit() ) {
330 return Bitstream.EOS;
331 }
332 media.position((int)newPosition);
333 pos = (int)newPosition;
334 if( posMark > pos ) {
335 posMark = -1;
336 }
337 return pos;
338 }
339
340 @Override
341 public long skip(final long n) {
342 final long skip;
343 if( n >= 0 ) {
344 final int remaining = media.limit() - pos;
345 skip = Math.min(remaining, (int)n);
346 } else {
347 final int n2 = (int)n * -1;
348 skip = -1 * Math.min(pos, n2);
349 }
350 pos += skip;
351 return skip;
352 }
353
354 @Override
355 public void mark(final int readLimit) {
356 posMark = pos;
357 }
358
359 @Override
360 public void reset() throws IllegalStateException {
361 if( 0 > posMark ) {
362 throw new IllegalStateException("markpos not set");
363 }
364 if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); }
365 media.position(posMark);
366 pos = posMark;
367 }
368
369 @Override
370 public int read() {
371 final int r;
372 if( media.limit() > pos ) {
373 r = 0xff & media.get(pos++);
374 } else {
375 r = -1; // EOS
376 }
377 if( DEBUG ) {
378 if( EOS != r ) {
379 System.err.println("u8["+(pos-1)+"] -> "+toHexBinString(true, r, 8));
380 } else {
381 System.err.println("u8["+(pos-0)+"] -> EOS");
382 }
383 }
384 return r;
385 }
386
387 @Override
388 public int write(final byte val) {
389 final int r;
390 if( media.limit() > pos ) {
391 media.put(pos++, val);
392 r = 0xff & val;
393 } else {
394 r = -1; // EOS
395 }
396 if( DEBUG ) {
397 if( EOS != r ) {
398 System.err.println("u8["+(pos-1)+"] <- "+toHexBinString(true, r, 8));
399 } else {
400 System.err.println("u8["+(pos-0)+"] <- EOS");
401 }
402 }
403 return r;
404 }
405 }
406
407 /**
408 * Specific {@link ByteStream byte stream}.
409 * <p>
410 * Can handle {@link #canInput() input} operations only.
411 * </p>
412 */
413 public static class ByteInputStream implements ByteStream<InputStream> {
414 private BufferedInputStream media;
415 private long pos;
416 private long posMark;
417
418 public ByteInputStream(final InputStream stream) {
419 setStream(stream);
420 }
421
422 @Override
423 public void setStream(final InputStream stream) {
424 if( stream instanceof BufferedInputStream ) {
425 media = (BufferedInputStream) stream;
426 } else if( null != stream ) {
427 media = new BufferedInputStream(stream);
428 } else {
429 media = null;
430 }
431 pos = 0;
432 posMark = -1;
433 }
434
435 @Override
436 public InputStream getStream() { return media; }
437
438 @Override
439 public void close() throws IOException {
440 if( null != media ) {
441 media.close();
442 media = null;
443 }
444 }
445 @Override
446 public void flush() {
447 // NOP
448 }
449
450 @Override
451 public boolean canInput() { return true; }
452
453 @Override
454 public boolean canOutput() { return false; }
455
456 @Override
457 public long position() { return pos; }
458
459 @Override
460 public long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException {
461 throw new UnsupportedOperationException("N/a for "+getClass().getCanonicalName());
462 }
463
464 @Override
465 public long skip(final long n) throws IOException {
466 final long skip = media.skip(n);
467 pos += skip;
468 return skip;
469 }
470
471 @Override
472 public void mark(final int readLimit) {
473 media.mark(readLimit);
474 posMark = pos;
475 }
476
477 @Override
478 public void reset() throws IllegalStateException, IOException {
479 if( 0 > posMark ) {
480 throw new IllegalStateException("markpos not set");
481 }
482 if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); }
483 media.reset();
484 pos = posMark;
485 }
486
487 @Override
488 public int read() throws IOException {
489 final int r = media.read();
490 if(DEBUG) {
491 if( EOS != r ) {
492 System.err.println("u8["+pos+"] -> "+toHexBinString(true, r, 8));
493 } else {
494 System.err.println("u8["+pos+"] -> EOS");
495 }
496 }
497 if( EOS != r ) {
498 pos++;
499 }
500 return r;
501 }
502
503 @Override
504 public int write(final byte val) throws UnsupportedOperationException {
505 throw new UnsupportedOperationException("not allowed with input stream");
506 }
507 }
508
509 /**
510 * Specific {@link ByteStream byte stream}.
511 * <p>
512 * Can handle {@link #canOutput() output} operations only.
513 * </p>
514 */
515 public static class ByteOutputStream implements ByteStream<OutputStream> {
516 private BufferedOutputStream media;
517 private long pos = 0;
518
519 public ByteOutputStream(final OutputStream stream) {
520 setStream(stream);
521 }
522
523 @Override
524 public void setStream(final OutputStream stream) {
525 if( stream instanceof BufferedOutputStream ) {
526 media = (BufferedOutputStream) stream;
527 } else if( null != stream ) {
528 media = new BufferedOutputStream(stream);
529 } else {
530 media = null;
531 }
532 pos = 0;
533 }
534
535 @Override
536 public void close() throws IOException {
537 if( null != media ) {
538 media.close();
539 media = null;
540 }
541 }
542 @Override
543 public void flush() throws IOException {
544 if( null != media ) {
545 media.flush();
546 }
547 }
548
549 @Override
550 public boolean canInput() { return false; }
551
552 @Override
553 public boolean canOutput() { return true; }
554
555 @Override
556 public long position() { return pos; }
557
558 @Override
559 public long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException {
560 throw new UnsupportedOperationException("N/a for "+getClass().getCanonicalName());
561 }
562
563 @Override
564 public long skip(final long n) throws IOException {
565 long i = n;
566 while(i > 0) {
567 media.write(0);
568 i--;
569 }
570 final long skip = n-i; // should be n
571 pos += skip;
572 return skip;
573 }
574
575 @Override
576 public OutputStream getStream() { return media; }
577
578 @Override
579 public void mark(final int readLimit) throws UnsupportedOperationException {
580 throw new UnsupportedOperationException("not allowed with output stream");
581 }
582
583 @Override
584 public void reset() throws UnsupportedOperationException {
585 throw new UnsupportedOperationException("not allowed with output stream");
586 }
587
588 @Override
589 public int read() throws UnsupportedOperationException {
590 throw new UnsupportedOperationException("not allowed with output stream");
591 }
592
593 @Override
594 public int write(final byte val) throws IOException {
595 final int r = 0xff & val;
596 media.write(r);
597 if(DEBUG) {
598 System.err.println("u8["+pos+"] <- "+toHexBinString(true, r, 8));
599 }
600 pos++;
601 return r;
602 }
603 }
604
605 private ByteStream<T> bytes;
606 /** 8-bit cache of byte stream */
607 private int bitBuffer;
608 private int bitsDataMark;
609
610 /** See {@link #getBitCount()}. */
611 private int bitCount;
612 private int bitsCountMark;
613
614 private boolean outputMode;
615 private boolean throwIOExceptionOnEOF;
616
617 /**
618 * @param stream
619 * @param outputMode
620 * @throws IllegalArgumentException if requested <i>outputMode</i> doesn't match stream's {@link #canInput()} and {@link #canOutput()}.
621 */
622 public Bitstream(final ByteStream<T> stream, final boolean outputMode) throws IllegalArgumentException {
623 this.bytes = stream;
624 this.outputMode = outputMode;
625 resetLocal();
626 validateMode();
627 throwIOExceptionOnEOF = false;
628 }
629
630 private final void resetLocal() {
631 bitBuffer = 0;
632 bitCount = 0;
633 bitsDataMark = 0;
634 bitsCountMark = -1;
635 }
636 private final void validateMode() throws IllegalArgumentException {
637 if( !canInput() && !canOutput() ) {
638 throw new IllegalArgumentException("stream can neither input nor output: "+this);
639 }
640 if( outputMode && !canOutput() ) {
641 throw new IllegalArgumentException("stream cannot output as requested: "+this);
642 }
643 if( !outputMode && !canInput() ) {
644 throw new IllegalArgumentException("stream cannot input as requested: "+this);
645 }
646 }
647
648 /**
649 * Enables or disables throwing an {@link IOException} in case {@link #EOS} appears.
650 * <p>
651 * Default behavior for I/O methods is not to throw an {@link IOException}, but to return {@link #EOS}.
652 * </p>
653 */
654 public final void setThrowIOExceptionOnEOF(final boolean enable) {
655 throwIOExceptionOnEOF = enable;
656 }
657
658 /** Returns true if I/O methods throw an {@link IOException} if {@link #EOS} appears, otherwise false (default). */
659 public final boolean getThrowIOExceptionOnEOF() { return throwIOExceptionOnEOF; }
660
661 /**
662 * Sets the underlying stream, without {@link #close()}ing the previous one.
663 * <p>
664 * If the previous stream was in {@link #canOutput() output mode},
665 * {@link #flush()} is being called.
666 * </p>
667 * @throws IllegalArgumentException if requested <i>outputMode</i> doesn't match stream's {@link #canInput()} and {@link #canOutput()}.
668 * @throws IOException could be caused by {@link #flush()}.
669 */
670 public final void setStream(final T stream, final boolean outputMode) throws IllegalArgumentException, IOException {
671 if( null != bytes && this.outputMode ) {
672 flush();
673 }
674 this.bytes.setStream(stream);
675 this.outputMode = outputMode;
676 resetLocal();
677 validateMode();
678 }
679
680 /** Returns the currently used {@link ByteStream}. */
681 public final ByteStream<T> getStream() { return bytes; }
682
683 /** Returns the currently used {@link ByteStream}'s {@link ByteStream#getStream()}. */
684 public final T getSubStream() { return bytes.getStream(); }
685
686 /**
687 * Closing the underlying stream, implies {@link #flush()}.
688 * <p>
689 * Implementation will <code>null</code> the stream references,
690 * hence {@link #setStream(Object)} must be called before re-using instance.
691 * </p>
692 * <p>
693 * If the closed stream was in {@link #canOutput() output mode},
694 * {@link #flush()} is being called.
695 * </p>
696 *
697 * @throws IOException
698 */
699 public final void close() throws IOException {
700 if( null != bytes && this.outputMode ) {
701 flush();
702 }
703 bytes.close();
704 bytes = null;
705 resetLocal();
706 }
707
708 /**
709 * Synchronizes all underlying {@link ByteStream#canOutput() output stream} operations, or do nothing.
710 * <p>
711 * Method also flushes incomplete bytes to the underlying {@link ByteStream}
712 * and hence skips to the next byte position.
713 * </p>
714 * @return {@link #EOS} caused by writing, otherwise zero.
715 * @throws IllegalStateException if not in output mode or stream closed
716 * @throws IOException
717 */
718 public final int flush() throws IllegalStateException, IOException {
719 if( !outputMode || null == bytes ) {
720 throw new IllegalStateException("not in output-mode: "+this);
721 }
722 bytes.flush();
723 if( 0 != bitCount ) {
724 final int r = bytes.write((byte)bitBuffer);
725 bitBuffer = 0;
726 bitCount = 0;
727 if( EOS == r ) {
728 if( throwIOExceptionOnEOF ) {
729 throw new IOException("EOS "+this);
730 }
731 return EOS;
732 }
733 }
734 return 0;
735 }
736
737 /** Return true if stream can handle input, i.e. {@link #readBit(boolean)}. */
738 public final boolean canInput() { return null != bytes ? bytes.canInput() : false; }
739
740 /** Return true if stream can handle output, i.e. {@link #writeBit(boolean, int)}. */
741 public final boolean canOutput() { return null != bytes ? bytes.canOutput() : false; }
742
743 /**
744 * Set {@code markpos} to current position, allowing the stream to be {@link #reset()}.
745 * @param readlimit maximum number of bytes able to read before invalidating the {@code markpos}.
746 * @throws IllegalStateException if not in input mode or stream closed
747 */
748 public final void mark(final int readLimit) throws IllegalStateException {
749 if( outputMode || null == bytes ) {
750 throw new IllegalStateException("not in input-mode: "+this);
751 }
752 bytes.mark(readLimit);
753 bitsDataMark = bitBuffer;
754 bitsCountMark = bitCount;
755 }
756
757 /**
758 * Reset stream position to <i>markpos</i> as set via {@link #mark(int)}.
759 * <p>
760 * <i>markpos</i> is kept, hence {@link #reset()} can be called multiple times.
761 * </p>
762 * @throws IllegalStateException if not in input mode or stream closed
763 * @throws IllegalStateException if <i>markpos</i> has not been set via {@link #mark(int)} or reset operation failed.
764 * @throws IOException if reset operation failed.
765 */
766 public final void reset() throws IllegalStateException, IOException {
767 if( outputMode || null == bytes ) {
768 throw new IllegalStateException("not in input-mode: "+this);
769 }
770 if( 0 > bitsCountMark ) {
771 throw new IllegalStateException("markpos not set: "+this);
772 }
773 bytes.reset();
774 bitBuffer = bitsDataMark;
775 bitCount = bitsCountMark;
776 }
777
778 /**
779 * Number of remaining bits in cache to read before next byte-read (input mode)
780 * or number of remaining bits to be cached before next byte-write (output mode).
781 * <p>
782 * Counting down from 7..0 7..0, starting with 0.
783 * </p>
784 * <p>
785 * In input mode, zero indicates reading a new byte and cont. w/ 7.
786 * In output mode, the cached byte is written when flipping over to 0.
787 * </p>
788 */
789 public final int getBitCount() { return bitCount; }
790
791 /**
792 * Return the last bit number read or written counting from [0..7].
793 * If no bit access has been performed, 7 is returned.
794 * <p>
795 * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order.
796 * </p>
797 */
798 public final int getLastBitPos() { return 7 - bitCount; }
799
800 /**
801 * Return the next bit number to be read or write counting from [0..7].
802 * If no bit access has been performed, 0 is returned.
803 * <p>
804 * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order.
805 * </p>
806 */
807 public final int getBitPosition() {
808 if( 0 == bitCount ) {
809 return 0;
810 } else {
811 return 8 - bitCount;
812 }
813 }
814
815 /**
816 * Returns the current bit buffer.
817 * @see #getBitCount()
818 */
819 public final int getBitBuffer() { return bitBuffer; }
820
821 /**
822 * Returns the bit position in the stream.
823 */
824 public final long position() {
825 // final long bytePos = bytes.position() - ( !outputMode && 0 != bitCount ? 1 : 0 );
826 // return ( bytePos << 3 ) + getBitPosition();
827 if( null == bytes ) {
828 return EOS;
829 } else if( 0 == bitCount ) {
830 return bytes.position() << 3;
831 } else {
832 final long bytePos = bytes.position() - ( outputMode ? 0 : 1 );
833 return ( bytePos << 3 ) + 8 - bitCount;
834 }
835 }
836
837 /**
838 * Sets this stream's bit position.
839 * <p>
840 * A set mark is cleared.
841 * </p>
842 * <p>
843 * Returns {@link Bitstream#EOS} is end-of-stream is reached,
844 * otherwise the new position.
845 * </p>
846 * <p>
847 * Known supporting implementation is {@link ByteBufferStream} and {@link ByteArrayStream}.
848 * </p>
849 *
850 * @param newPosition The new positive position.
851 *
852 * @return The new set position or {@link Bitstream#EOS} if end-of-stream is reached.
853 *
854 * @throws UnsupportedOperationException if not supported, i.e. {@link ByteInputStream} or {@link ByteOutputStream}
855 * @throws IllegalArgumentException If the {@code newPosition} is negative
856 * @throws IOException if read error occurs or EOS is reached and {@link #setThrowIOExceptionOnEOF(boolean)} is set to true.
857 * @throws IllegalStateException
858 */
859 public final long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException, IOException {
860 if( 0 > newPosition ) {
861 throw new IllegalArgumentException("new position not positive: "+newPosition);
862 }
863 bytes.position(0); // throws UnsupportedOperationException
864 resetLocal();
865 if( newPosition > skip(newPosition) ) {
866 return EOS;
867 }
868 return newPosition;
869 }
870
871 /**
872 * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB.
873 * @return the read bit or {@link #EOS} if end-of-stream is reached.
874 * @throws IOException
875 * @throws IllegalStateException if not in input mode or stream closed
876 */
877 public final int readBit(final boolean msbFirst) throws UnsupportedOperationException, IllegalStateException, IOException {
878 if( outputMode || null == bytes ) {
879 throw new IllegalStateException("not in input-mode: "+this);
880 }
881 if ( 0 < bitCount ) {
882 bitCount--;
883 if( msbFirst ) {
884 return ( bitBuffer >>> bitCount ) & 0x01;
885 } else {
886 return ( bitBuffer >>> ( 7 - bitCount ) ) & 0x01;
887 }
888 } else {
889 bitBuffer = bytes.read();
890 if( EOS == bitBuffer ) {
891 if( throwIOExceptionOnEOF ) {
892 throw new IOException("EOS "+this);
893 }
894 return EOS;
895 } else {
896 bitCount=7;
897 if( msbFirst ) {
898 return bitBuffer >>> 7;
899 } else {
900 return bitBuffer & 0x01;
901 }
902 }
903 }
904 }
905
906 /**
907 * @param msbFirst if true outgoing stream bit order is MSB to LSB, otherwise LSB to MSB.
908 * @param bit
909 * @return the currently written byte or {@link #EOS} if end-of-stream is reached.
910 * @throws IOException
911 * @throws IllegalStateException if not in output mode or stream closed
912 */
913 public final int writeBit(final boolean msbFirst, final int bit) throws IllegalStateException, IOException {
914 if( !outputMode || null == bytes ) {
915 throw new IllegalStateException("not in output-mode: "+this);
916 }
917 if ( 0 < bitCount ) {
918 bitCount--;
919 if( msbFirst ) {
920 bitBuffer |= ( 0x01 & bit ) << bitCount;
921 } else {
922 bitBuffer |= ( 0x01 & bit ) << ( 7 - bitCount );
923 }
924 if( 0 == bitCount ) {
925 final int r = bytes.write((byte)bitBuffer);
926 if( throwIOExceptionOnEOF && EOS == r ) {
927 throw new IOException("EOS "+this);
928 }
929 return r;
930 }
931 } else {
932 bitCount = 7;
933 if( msbFirst ) {
934 bitBuffer = ( 0x01 & bit ) << 7;
935 } else {
936 bitBuffer = 0x01 & bit;
937 }
938 }
939 return bitBuffer;
940 }
941
942 /**
943 * It is implementation dependent, whether backward skip giving a negative number is supported or not.
944 *
945 * @param n number of bits to skip
946 * @return actual skipped bits
947 * @throws IOException if read error occurs or EOS is reached and {@link #setThrowIOExceptionOnEOF(boolean)} is set to true.
948 * @throws IllegalStateException if closed
949 */
950 public long skip(final long n) throws IllegalStateException, IOException {
951 if( null == bytes ) {
952 throw new IllegalStateException("closed: "+this);
953 }
954 if( DEBUG ) {
955 System.err.println("Bitstream.skip.0: "+n+" - "+toStringImpl());
956 }
957 if( n > 0 ) {
958 if( n <= bitCount ) {
959 bitCount -= (int)n;
960 if( DEBUG ) {
961 System.err.println("Bitstream.skip.F_N1: "+n+" - "+toStringImpl());
962 }
963 return n;
964 } else { // n > bitCount
965 if( outputMode ) {
966 if( 0 < bitCount ) {
967 if( EOS == bytes.write((byte)bitBuffer) ) {
968 return 0;
969 }
970 }
971 bitBuffer = 0;
972 }
973 final long n2 = n - bitCount; // subtract cached bits, bitsCount is zero at this point
974 final long n3 = n2 >>> 3; // bytes to skip
975 final long n4 = bytes.skip(n3); // actual skipped bytes
976 final int n5 = (int) ( n2 - ( n3 << 3 ) ); // remaining skip bits == nX % 8
977 final long nX = ( n4 << 3 ) + n5 + bitCount; // actual skipped bits
978 /**
979 if( DEBUG ) {
980 System.err.println("Bitstream.skip.1: n2 "+n2+", n3 "+n3+", n4 "+n4+", n5 "+n5+", nX "+nX+" - "+toStringImpl());
981 } */
982 if( nX < n ) {
983 // couldn't complete skipping .. EOS .. etc
984 bitCount = 0;
985 bitBuffer = 0;
986 if( DEBUG ) {
987 System.err.println("Bitstream.skip.F_EOS: "+n+" - "+toStringImpl());
988 }
989 if( throwIOExceptionOnEOF ) {
990 throw new IOException("EOS "+this);
991 }
992 return nX;
993 }
994 bitCount = ( 8 - n5 ) & 7; // % 8
995 int notReadBits = 0;
996 if( !outputMode && 0 < bitCount ) {
997 bitBuffer = bytes.read();
998 if( EOS == bitBuffer ) {
999 notReadBits = bitCount;
1000 bitCount = 0;
1001 }
1002 }
1003 if( DEBUG ) {
1004 System.err.println("Bitstream.skip.F_N2: "+n+", notReadBits "+notReadBits+" - "+toStringImpl());
1005 }
1006 return nX - notReadBits;
1007 }
1008 } else {
1009 // Zero skip or backward skip
1010 // FIXME: Backward skip n < 0
1011 return 0;
1012 }
1013 }
1014
1015 private static final boolean useFastPathStream = true;
1016 private static final boolean useFastPathTypes = true;
1017
1018 /**
1019 * Return incoming bits as read via {@link #readBit(boolean)} LSB-first as little-endian.
1020 * <p>
1021 * The incoming bit order is from low- to most-significant-bit, maintaining bit LSB-first order.
1022 * </p>
1023 * @param n number of bits, maximum 31 bits
1024 * @return the read bits from 0-n in the given order or {@link #EOS}.
1025 * @throws IllegalStateException if not in input mode or stream closed
1026 * @throws IllegalArgumentException if n > 31
1027 * @throws IOException
1028 */
1029 public int readBits31(final int n) throws IllegalArgumentException, IOException {
1030 if( 31 < n ) {
1031 throw new IllegalArgumentException("n > 31: "+n);
1032 }
1033 if( outputMode || null == bytes ) {
1034 throw new IllegalStateException("not in input-mode: "+this);
1035 }
1036 if( 0 == n ) {
1037 return 0;
1038 } else {
1039 if( !useFastPathStream ) {
1040 // Slow path
1041 int r = 0;
1042 for(int i=0; i < n; i++) {
1043 final int b = readBit(false /* msbFirst */);
1044 if( EOS == b ) {
1045 if( throwIOExceptionOnEOF ) {
1046 throw new IOException("EOS "+this);
1047 }
1048 return EOS;
1049 }
1050 r |= b << i;
1051 }
1052 return r;
1053 } else {
1054 // fast path
1055 int c = n;
1056 final int n1 = Math.min(n, bitCount); // remaining portion
1057 int r;
1058 if( 0 < n1 ) {
1059 final int m1 = ( 1 << n1 ) - 1;
1060 final int s1 = 7 - bitCount + 1; // LSBfirst: right-shift to new bits
1061 bitCount -= n1;
1062 c -= n1;
1063 // MSBfirst: r = ( m1 & ( bitBuffer >>> bitCount ) ) << c;
1064 r = ( m1 & ( bitBuffer >>> s1 ) ); // LSBfirst
1065 if( 0 == c ) {
1066 return r;
1067 }
1068 } else {
1069 r = 0;
1070 }
1071 assert( 0 == bitCount );
1072 int s = n1; // LSBfirst: left shift for additional elements
1073 do {
1074 bitBuffer = bytes.read();
1075 if( EOS == bitBuffer ) {
1076 if( throwIOExceptionOnEOF ) {
1077 throw new IOException("EOS "+this);
1078 }
1079 return EOS;
1080 }
1081 final int n2 = Math.min(c, 8); // full portion
1082 final int m2 = ( 1 << n2 ) - 1;
1083 bitCount = 8 - n2;
1084 c -= n2;
1085 // MSBfirst: r |= ( m2 & ( bitBuffer >>> bitCount ) ) << c;
1086 r |= ( m2 & bitBuffer ) << s; // LSBfirst on new bits
1087 s += n2;
1088 } while ( 0 < c );
1089 return r;
1090 }
1091 }
1092 }
1093
1094 /**
1095 * Write the given bits via {@link #writeBit(boolean, int)} LSB-first as little-endian.
1096 * <p>
1097 * The outgoing bit order is from low- to most-significant-bit, maintaining bit LSB-first order.
1098 * </p>
1099 * @param n number of bits, maximum 31 bits
1100 * @param bits the bits to write
1101 * @return the written bits or {@link #EOS}.
1102 * @throws IllegalStateException if not in output mode or stream closed
1103 * @throws IllegalArgumentException if n > 31
1104 * @throws IOException
1105 */
1106 public int writeBits31(final int n, final int bits) throws IllegalStateException, IllegalArgumentException, IOException {
1107 if( 31 < n ) {
1108 throw new IllegalArgumentException("n > 31: "+n);
1109 }
1110 if( !outputMode || null == bytes ) {
1111 throw new IllegalStateException("not in output-mode: "+this);
1112 }
1113 if( 0 < n ) {
1114 if( !useFastPathStream ) {
1115 // Slow path
1116 for(int i=0; i < n; i++) {
1117 final int b = writeBit(false /* msbFirst */, ( bits >>> i ) & 0x1);
1118 if( EOS == b ) {
1119 return EOS;
1120 }
1121 }
1122 } else {
1123 // fast path
1124 int c = n;
1125 final int n1 = Math.min(n, bitCount); // remaining portion
1126 if( 0 < n1 ) {
1127 final int m1 = ( 1 << n1 ) - 1;
1128 final int s1 = 7 - bitCount + 1; // LSBfirst: left-shift to free bit-pos
1129 bitCount -= n1;
1130 c -= n1;
1131 // MSBfirst: bitBuffer |= ( m1 & ( bits >>> c ) ) << bitCount;
1132 bitBuffer |= ( m1 & bits ) << s1 ; // LSBfirst
1133 if( 0 == bitCount ) {
1134 if( EOS == bytes.write((byte)bitBuffer) ) {
1135 if( throwIOExceptionOnEOF ) {
1136 throw new IOException("EOS "+this);
1137 }
1138 return EOS;
1139 }
1140 }
1141 if( 0 == c ) {
1142 return bits;
1143 }
1144 }
1145 assert( 0 == bitCount );
1146 int s = n1; // LSBfirst: left shift for additional elements
1147 do {
1148 final int n2 = Math.min(c, 8); // full portion
1149 final int m2 = ( 1 << n2 ) - 1;
1150 bitCount = 8 - n2;
1151 c -= n2;
1152 // MSBfirst: bitBuffer = ( m2 & ( bits >>> c ) ) << bitCount;
1153 bitBuffer = ( m2 & ( bits >>> s ) ); // LSBfirst
1154 s += n2;
1155 if( 0 == bitCount ) {
1156 if( EOS == bytes.write((byte)bitBuffer) ) {
1157 if( throwIOExceptionOnEOF ) {
1158 throw new IOException("EOS "+this);
1159 }
1160 return EOS;
1161 }
1162 }
1163 } while ( 0 < c );
1164 }
1165 }
1166 return bits;
1167 }
1168
1169 /**
1170 * Return incoming <code>uint8_t</code> as read via {@link #readBits31(int)}.
1171 * <p>
1172 * In case of a <code>int8_t</code> 2-complement signed value, simply cast the result to <code>byte</code>
1173 * after checking for {@link #EOS}.
1174 * </p>
1175 * @return {@link #EOS} or the 8bit unsigned value within the lower bits.
1176 * @throws IllegalStateException if not in input mode or stream closed
1177 * @throws IOException
1178 */
1179 public final int readUInt8() throws IllegalStateException, IOException {
1180 if( 0 == bitCount && useFastPathTypes ) {
1181 // fast path
1182 if( outputMode || null == bytes ) {
1183 throw new IllegalStateException("not in input-mode: "+this);
1184 }
1185 final int r = bytes.read();
1186 if( throwIOExceptionOnEOF && EOS == r ) {
1187 throw new IOException("EOS "+this);
1188 }
1189 return r;
1190 } else {
1191 return readBits31(8);
1192 }
1193 }
1194
1195 /**
1196 * Write the given 8 bits via {@link #writeBits31(int, int)}.
1197 * @return {@link #EOS} or the written 8bit value.
1198 * @throws IllegalStateException if not in output mode or stream closed
1199 * @throws IOException
1200 */
1201 public final int writeInt8(final byte int8) throws IllegalStateException, IOException {
1202 if( 0 == bitCount && useFastPathTypes ) {
1203 // fast path
1204 if( !outputMode || null == bytes ) {
1205 throw new IllegalStateException("not in output-mode: "+this);
1206 }
1207 final int r = bytes.write(int8);
1208 if( throwIOExceptionOnEOF && EOS == r ) {
1209 throw new IOException("EOS "+this);
1210 }
1211 return r;
1212 } else {
1213 return this.writeBits31(8, int8);
1214 }
1215 }
1216
1217 /**
1218 * Return incoming <code>uint16_t</code> as read via {@link #readBits31(int)} LSB-first as little-endian,
1219 * hence bytes are swapped if bigEndian.
1220 * <p>
1221 * In case of a <code>int16_t</code> 2-complement signed value, simply cast the result to <code>short</code>
1222 * after checking for {@link #EOS}.
1223 * </p>
1224 * @param bigEndian if true, swap incoming bytes to little-endian, otherwise leave them as little-endian.
1225 * @return {@link #EOS} or the 16bit unsigned value within the lower bits.
1226 * @throws IllegalStateException if not in input mode or stream closed
1227 * @throws IOException
1228 */
1229 public final int readUInt16(final boolean bigEndian) throws IllegalStateException, IOException {
1230 if( 0 == bitCount && useFastPathTypes ) {
1231 // fast path
1232 if( outputMode || null == bytes ) {
1233 throw new IllegalStateException("not in input-mode: "+this);
1234 }
1235 final int b1 = bytes.read();
1236 final int b2 = EOS != b1 ? bytes.read() : EOS;
1237 if( EOS == b2 ) {
1238 if( throwIOExceptionOnEOF ) {
1239 throw new IOException("EOS "+this);
1240 }
1241 return EOS;
1242 } else if( bigEndian ) {
1243 return b1 << 8 | b2;
1244 } else {
1245 return b2 << 8 | b1;
1246 }
1247 } else {
1248 final int i16 = readBits31(16);
1249 if( EOS == i16 ) {
1250 return EOS;
1251 } else if( bigEndian ) {
1252 final int b1 = 0xff & ( i16 >>> 8 );
1253 final int b2 = 0xff & i16;
1254 return b2 << 8 | b1;
1255 } else {
1256 return i16;
1257 }
1258 }
1259 }
1260
1261 /**
1262 * Return incoming <code>uint16_t</code> value and swap bytes according to bigEndian.
1263 * <p>
1264 * In case of a <code>int16_t</code> 2-complement signed value, simply cast the result to <code>short</code>.
1265 * </p>
1266 * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as big-endian.
1267 * @return the 16bit unsigned value within the lower bits.
1268 * @throws IndexOutOfBoundsException
1269 */
1270 public static final int readUInt16(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException {
1271 checkBounds(bytes, offset, 2);
1272 // Make sure we clear any high bits that get set in sign-extension
1273 final int b1 = bytes[offset] & 0xff;
1274 final int b2 = bytes[offset+1] & 0xff;
1275 if( bigEndian ) {
1276 return b1 << 8 | b2;
1277 } else {
1278 return b2 << 8 | b1;
1279 }
1280 }
1281
1282 /**
1283 * Write the given 16 bits via {@link #writeBits31(int, int)} LSB-first as little-endian,
1284 * hence bytes are swapped if bigEndian.
1285 * @param bigEndian if true, swap given bytes to little-endian, otherwise leave them as little-endian.
1286 * @return {@link #EOS} or the written 16bit value.
1287 * @throws IllegalStateException if not in output mode or stream closed
1288 * @throws IOException
1289 */
1290 public final int writeInt16(final boolean bigEndian, final short int16) throws IllegalStateException, IOException {
1291 if( 0 == bitCount && useFastPathTypes ) {
1292 // fast path
1293 if( !outputMode || null == bytes ) {
1294 throw new IllegalStateException("not in output-mode: "+this);
1295 }
1296 final byte hi = (byte) ( 0xff & ( int16 >>> 8 ) );
1297 final byte lo = (byte) ( 0xff & int16 );
1298 final byte b1, b2;
1299 if( bigEndian ) {
1300 b1 = hi;
1301 b2 = lo;
1302 } else {
1303 b1 = lo;
1304 b2 = hi;
1305 }
1306 if( EOS != bytes.write(b1) ) {
1307 if( EOS != bytes.write(b2) ) {
1308 return int16;
1309 }
1310 }
1311 if( throwIOExceptionOnEOF ) {
1312 throw new IOException("EOS "+this);
1313 }
1314 return EOS;
1315 } else if( bigEndian ) {
1316 final int b1 = 0xff & ( int16 >>> 8 );
1317 final int b2 = 0xff & int16;
1318 return writeBits31(16, b2 << 8 | b1);
1319 } else {
1320 return writeBits31(16, int16);
1321 }
1322 }
1323
1324 /**
1325 * Return incoming <code>uint32_t</code> as read via {@link #readBits31(int)} LSB-first as little-endian,
1326 * hence bytes are swapped if bigEndian.
1327 * <p>
1328 * In case of a <code>int32_t</code> 2-complement signed value, simply cast the result to <code>int</code>
1329 * after checking for {@link #EOS}.
1330 * </p>
1331 * @param bigEndian if true, swap incoming bytes to little-endian, otherwise leave them as little-endian.
1332 * @return {@link #EOS} or the 32bit unsigned value within the lower bits.
1333 * @throws IllegalStateException if not in input mode or stream closed
1334 * @throws IOException
1335 */
1336 public final long readUInt32(final boolean bigEndian) throws IllegalStateException, IOException {
1337 if( 0 == bitCount && useFastPathTypes ) {
1338 // fast path
1339 if( outputMode || null == bytes ) {
1340 throw new IllegalStateException("not in input-mode: "+this);
1341 }
1342 final int b1 = bytes.read();
1343 final int b2 = EOS != b1 ? bytes.read() : EOS;
1344 final int b3 = EOS != b2 ? bytes.read() : EOS;
1345 final int b4 = EOS != b3 ? bytes.read() : EOS;
1346 if( EOS == b4 ) {
1347 if( throwIOExceptionOnEOF ) {
1348 throw new IOException("EOS "+this);
1349 }
1350 return EOS;
1351 } else if( bigEndian ) {
1352 return 0xffffffffL & ( b1 << 24 | b2 << 16 | b3 << 8 | b4 );
1353 } else {
1354 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1355 }
1356 } else {
1357 final int i16a = readBits31(16);
1358 final int i16b = EOS != i16a ? readBits31(16) : EOS;
1359 if( EOS == i16b ) {
1360 return EOS;
1361 } else if( bigEndian ) {
1362 final int b1 = 0xff & ( i16b >>> 8 );
1363 final int b2 = 0xff & i16b;
1364 final int b3 = 0xff & ( i16a >>> 8 );
1365 final int b4 = 0xff & i16a;
1366 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1367 } else {
1368 return 0xffffffffL & ( i16b << 16 | i16a );
1369 }
1370 }
1371 }
1372
1373 /**
1374 * Return incoming <code>uint32_t</code> and swap bytes according to bigEndian.
1375 * <p>
1376 * In case of a <code>int32_t</code> 2-complement signed value, simply cast the result to <code>int</code>.
1377 * </p>
1378 * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as big-endian.
1379 * @return the 32bit unsigned value within the lower bits.
1380 * @throws IndexOutOfBoundsException
1381 */
1382 public static final long readUInt32(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException {
1383 checkBounds(bytes, offset, 4);
1384 final int b1 = bytes[offset];
1385 final int b2 = bytes[offset+1];
1386 final int b3 = bytes[offset+2];
1387 final int b4 = bytes[offset+3];
1388 if( bigEndian ) {
1389 return 0xffffffffL & ( b1 << 24 | b2 << 16 | b3 << 8 | b4 );
1390 } else {
1391 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1392 }
1393 }
1394
1395 /**
1396 * Write the given 32 bits via {@link #writeBits31(int, int)} LSB-first as little-endian,
1397 * hence bytes are swapped if bigEndian.
1398 * @param bigEndian if true, swap given bytes to little-endian, otherwise leave them as little-endian.
1399 * @return {@link #EOS} or the written 32bit value.
1400 * @throws IllegalStateException if not in output mode or stream closed
1401 * @throws IOException
1402 */
1403 public final int writeInt32(final boolean bigEndian, final int int32) throws IllegalStateException, IOException {
1404 if( 0 == bitCount && useFastPathTypes ) {
1405 // fast path
1406 if( !outputMode || null == bytes ) {
1407 throw new IllegalStateException("not in output-mode: "+this);
1408 }
1409 final byte p1 = (byte) ( 0xff & ( int32 >>> 24 ) );
1410 final byte p2 = (byte) ( 0xff & ( int32 >>> 16 ) );
1411 final byte p3 = (byte) ( 0xff & ( int32 >>> 8 ) );
1412 final byte p4 = (byte) ( 0xff & int32 );
1413 final byte b1, b2, b3, b4;
1414 if( bigEndian ) {
1415 b1 = p1;
1416 b2 = p2;
1417 b3 = p3;
1418 b4 = p4;
1419 } else {
1420 b1 = p4;
1421 b2 = p3;
1422 b3 = p2;
1423 b4 = p1;
1424 }
1425 if( EOS != bytes.write(b1) ) {
1426 if( EOS != bytes.write(b2) ) {
1427 if( EOS != bytes.write(b3) ) {
1428 if( EOS != bytes.write(b4) ) {
1429 return int32;
1430 }
1431 }
1432 }
1433 }
1434 if( throwIOExceptionOnEOF ) {
1435 throw new IOException("EOS "+this);
1436 }
1437 return EOS;
1438 } else if( bigEndian ) {
1439 final int p1 = 0xff & ( int32 >>> 24 );
1440 final int p2 = 0xff & ( int32 >>> 16 );
1441 final int p3 = 0xff & ( int32 >>> 8 );
1442 final int p4 = 0xff & int32 ;
1443 if( EOS != writeBits31(16, p2 << 8 | p1) ) {
1444 if( EOS != writeBits31(16, p4 << 8 | p3) ) {
1445 return int32;
1446 }
1447 }
1448 return EOS;
1449 } else {
1450 final int hi = 0x0000ffff & ( int32 >>> 16 );
1451 final int lo = 0x0000ffff & int32 ;
1452 if( EOS != writeBits31(16, lo) ) {
1453 if( EOS != writeBits31(16, hi) ) {
1454 return int32;
1455 }
1456 }
1457 return EOS;
1458 }
1459 }
1460
1461 /**
1462 * Reinterpret the given <code>int32_t</code> value as <code>uint32_t</code>,
1463 * i.e. perform the following cast to <code>long</code>:
1464 * <pre>
1465 * final long l = 0xffffffffL & int32;
1466 * </pre>
1467 */
1468 public static final long toUInt32Long(final int int32) {
1469 return 0xffffffffL & int32;
1470 }
1471
1472 /**
1473 * Returns the reinterpreted given <code>int32_t</code> value
1474 * as <code>uint32_t</code> if &le; {@link Integer#MAX_VALUE}
1475 * as within an <code>int</code> storage.
1476 * Otherwise return -1.
1477 */
1478 public static final int toUInt32Int(final int int32) {
1479 return uint32LongToInt(toUInt32Long(int32));
1480 }
1481
1482 /**
1483 * Returns the given <code>uint32_t</code> value <code>long</code>
1484 * value as <code>int</code> if &le; {@link Integer#MAX_VALUE}.
1485 * Otherwise return -1.
1486 */
1487 public static final int uint32LongToInt(final long uint32) {
1488 if( Integer.MAX_VALUE >= uint32 ) {
1489 return (int)uint32;
1490 } else {
1491 return -1;
1492 }
1493 }
1494
1495 @Override
1496 public String toString() {
1497 return String.format("Bitstream[%s]", toStringImpl());
1498 }
1499 protected String toStringImpl() {
1500 final String mode;
1501 final long bpos;
1502 if( null == bytes ) {
1503 mode = "closed";
1504 bpos = -1;
1505 } else {
1506 mode = outputMode ? "output" : "input";
1507 bpos = bytes.position();
1508 }
1509 return String.format((Locale)null, "%s, pos %d [byteP %d, bitCnt %d], bitbuf %s",
1510 mode, position(), bpos, bitCount, toHexBinString(true, bitBuffer, 8));
1511 }
1512
1513 private static final String strZeroPadding= "0000000000000000000000000000000000000000000000000000000000000000"; // 64
1514 public static String toBinString(final boolean msbFirst, final int v, final int bitCount) {
1515 if( 0 == bitCount ) {
1516 return "";
1517 }
1518 if( msbFirst ) {
1519 final int mask = (int) ( ( 1L << bitCount ) - 1L );
1520 final String s0 = Integer.toBinaryString( mask & v );
1521 return strZeroPadding.substring(0, bitCount-s0.length())+s0;
1522 } else {
1523 final char[] c = new char[32];
1524 for(int i=0; i<bitCount; i++) {
1525 c[i] = 0 != ( v & ( 1 << i ) ) ? '1' : '0';
1526 }
1527 final String s0 = new String(c, 0, bitCount);
1528 return s0+strZeroPadding.substring(0, bitCount-s0.length());
1529 }
1530 }
1531 public static String toHexBinString(final boolean msbFirst, final int v, final int bitCount) {
1532 final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4;
1533 return String.format((Locale)null, "[0x%0"+nibbles+"X, msbFirst %b, %s]", v, msbFirst, toBinString(msbFirst, v, bitCount));
1534 }
1535 public static final String toHexBinString(final boolean msbFirst, final byte[] data, final int offset, final int len) {
1536 final StringBuilder sb = new StringBuilder();
1537 sb.append("[");
1538 for(int i=0; i<len; i++) {
1539 final int v = 0xFF & data[offset+i];
1540 sb.append(toHexBinString(msbFirst, v, 8)).append(", ");
1541 }
1542 sb.append("]");
1543 return sb.toString();
1544 }
1545 public static final String toHexBinString(final boolean msbFirst, final ByteBuffer data, final int offset, final int len) {
1546 final StringBuilder sb = new StringBuilder();
1547 sb.append("[");
1548 for(int i=0; i<len; i++) {
1549 final int v = 0xFF & data.get(offset+i);
1550 sb.append(toHexBinString(msbFirst, v, 8)).append(", ");
1551 }
1552 sb.append("]");
1553 return sb.toString();
1554 }
1555
1556 public static void checkBounds(final byte[] sb, final int offset, final int remaining) throws IndexOutOfBoundsException {
1557 if( offset + remaining > sb.length ) {
1558 throw new IndexOutOfBoundsException("Buffer of size "+sb.length+" cannot hold offset "+offset+" + remaining "+remaining);
1559 }
1560 }
1561}
long position(final long newPosition)
Definition: Bitstream.java:207
void setStream(final byte[] stream)
Definition: Bitstream.java:179
ByteArrayStream(final byte[] stream)
Definition: Bitstream.java:174
void mark(final int readLimit)
Definition: Bitstream.java:233
void setStream(final ByteBuffer stream)
Definition: Bitstream.java:300
void mark(final int readLimit)
Definition: Bitstream.java:355
ByteBufferStream(final ByteBuffer stream)
Definition: Bitstream.java:295
long position(final long newPosition)
Definition: Bitstream.java:328
void mark(final int readLimit)
Definition: Bitstream.java:472
void setStream(final InputStream stream)
Definition: Bitstream.java:423
ByteInputStream(final InputStream stream)
Definition: Bitstream.java:418
long position(final long newPosition)
Definition: Bitstream.java:460
void setStream(final OutputStream stream)
Definition: Bitstream.java:524
void mark(final int readLimit)
Definition: Bitstream.java:579
long position(final long newPosition)
Definition: Bitstream.java:559
ByteOutputStream(final OutputStream stream)
Definition: Bitstream.java:519
Versatile Bitstream implementation supporting:
Definition: Bitstream.java:49
int readBits31(final int n)
Return incoming bits as read via readBit(boolean) LSB-first as little-endian.
final void mark(final int readLimit)
Set markpos to current position, allowing the stream to be reset().
Definition: Bitstream.java:748
final ByteStream< T > getStream()
Returns the currently used ByteStream.
Definition: Bitstream.java:681
final int flush()
Synchronizes all underlying output stream operations, or do nothing.
Definition: Bitstream.java:718
static final int toUInt32Int(final int int32)
Returns the reinterpreted given int32_t value as uint32_t if ≤ Integer#MAX_VALUE as within an int sto...
final int writeInt8(final byte int8)
Write the given 8 bits via writeBits31(int, int).
static final int readUInt16(final boolean bigEndian, final byte[] bytes, final int offset)
Return incoming uint16_t value and swap bytes according to bigEndian.
int writeBits31(final int n, final int bits)
Write the given bits via writeBit(boolean, int) LSB-first as little-endian.
final int getBitPosition()
Return the next bit number to be read or write counting from [0..7].
Definition: Bitstream.java:807
final void reset()
Reset stream position to markpos as set via mark(int).
Definition: Bitstream.java:766
final int readUInt16(final boolean bigEndian)
Return incoming uint16_t as read via readBits31(int) LSB-first as little-endian, hence bytes are swap...
final int writeInt16(final boolean bigEndian, final short int16)
Write the given 16 bits via writeBits31(int, int) LSB-first as little-endian, hence bytes are swapped...
final int getLastBitPos()
Return the last bit number read or written counting from [0..7].
Definition: Bitstream.java:798
final boolean canOutput()
Return true if stream can handle output, i.e.
Definition: Bitstream.java:741
static String toHexBinString(final boolean msbFirst, final int v, final int bitCount)
static final int uint32LongToInt(final long uint32)
Returns the given uint32_t value long value as int if ≤ Integer#MAX_VALUE.
final long position()
Returns the bit position in the stream.
Definition: Bitstream.java:824
final boolean canInput()
Return true if stream can handle input, i.e.
Definition: Bitstream.java:738
static final long readUInt32(final boolean bigEndian, final byte[] bytes, final int offset)
Return incoming uint32_t and swap bytes according to bigEndian.
static final String toHexBinString(final boolean msbFirst, final byte[] data, final int offset, final int len)
static final long toUInt32Long(final int int32)
Reinterpret the given int32_t value as uint32_t, i.e.
final boolean getThrowIOExceptionOnEOF()
Returns true if I/O methods throw an IOException if EOS appears, otherwise false (default).
Definition: Bitstream.java:659
final int getBitBuffer()
Returns the current bit buffer.
Definition: Bitstream.java:819
long skip(final long n)
It is implementation dependent, whether backward skip giving a negative number is supported or not.
Definition: Bitstream.java:950
final long position(final long newPosition)
Sets this stream's bit position.
Definition: Bitstream.java:859
final void setThrowIOExceptionOnEOF(final boolean enable)
Enables or disables throwing an IOException in case EOS appears.
Definition: Bitstream.java:654
final void setStream(final T stream, final boolean outputMode)
Sets the underlying stream, without close()ing the previous one.
Definition: Bitstream.java:670
final int readBit(final boolean msbFirst)
Definition: Bitstream.java:877
Bitstream(final ByteStream< T > stream, final boolean outputMode)
Definition: Bitstream.java:622
final long readUInt32(final boolean bigEndian)
Return incoming uint32_t as read via readBits31(int) LSB-first as little-endian, hence bytes are swap...
final void close()
Closing the underlying stream, implies flush().
Definition: Bitstream.java:699
static String toBinString(final boolean msbFirst, final int v, final int bitCount)
static final String toHexBinString(final boolean msbFirst, final ByteBuffer data, final int offset, final int len)
final T getSubStream()
Returns the currently used ByteStream's ByteStream#getStream().
Definition: Bitstream.java:684
final int writeInt32(final boolean bigEndian, final int int32)
Write the given 32 bits via writeBits31(int, int) LSB-first as little-endian, hence bytes are swapped...
static final int EOS
End of stream marker, {@value} or 0xFFFFFFFF.
Definition: Bitstream.java:53
final int getBitCount()
Number of remaining bits in cache to read before next byte-read (input mode) or number of remaining b...
Definition: Bitstream.java:789
static void checkBounds(final byte[] sb, final int offset, final int remaining)
final int writeBit(final boolean msbFirst, final int bit)
Definition: Bitstream.java:913
final int readUInt8()
Return incoming uint8_t as read via readBits31(int).
Helper routines for logging and debugging.
Definition: Debug.java:35
static final boolean debug(final String subcomponent)
Definition: Debug.java:63
General byte stream.
Definition: Bitstream.java:58
long skip(final long n)
It is implementation dependent, whether backward skip giving a negative number is supported or not.
void flush()
Synchronizes all underlying output stream operations, or do nothing.
void reset()
Reset stream position to markpos as set via mark(int).
boolean canOutput()
Return true if stream can handle output, i.e.
void close()
Closing the underlying stream, implies flush().
int read()
Reads one byte from the stream.
long position()
Returns the byte position in the stream.
void setStream(final T stream)
Sets the underlying stream, without close()ing the previous one.
void mark(final int readLimit)
Set markpos to current position, allowing the stream to be reset().
boolean canInput()
Return true if stream can handle input, i.e.
T getStream()
Returns the underlying stream.
int write(final byte val)
Writes one byte, to the stream.