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