jaulib v1.4.0-2-g788cf73
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
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> m_bytes;
606 /** 8-bit cache of byte stream */
607 private int m_bitCache;
608 private int m_bitsDataMark;
609
610 /** See {@link #getCachedBitCount()}. */
611 private int m_bitCount;
612 private int m_bitsCountMark;
613
614 private boolean m_writeMode;
615 private boolean m_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 #canWrite()}.
621 */
622 public Bitstream(final ByteStream<T> stream, final boolean outputMode) throws IllegalArgumentException {
623 this.m_bytes = stream;
624 this.m_writeMode = outputMode;
625 resetLocal();
626 validateMode();
627 m_throwIOExceptionOnEOF = false;
628 }
629
630 private final void resetLocal() {
631 m_bitCache = 0;
632 m_bitCount = 0;
633 m_bitsDataMark = 0;
634 m_bitsCountMark = -1;
635 }
636 private final boolean canInput0() { return null != m_bytes ? m_bytes.canInput() : false; }
637 private final boolean canOutput0() { return null != m_bytes ? m_bytes.canOutput() : false; }
638 private final void validateMode() throws IllegalArgumentException {
639 if( !canInput0() && !canOutput0() ) {
640 throw new IllegalArgumentException("stream can neither input nor output: "+this);
641 }
642 if( m_writeMode && !canOutput0() ) {
643 throw new IllegalArgumentException("stream cannot output as requested: "+this);
644 }
645 if( !m_writeMode && !canInput0() ) {
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 m_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 m_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 #canWrite() 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 #canWrite()}.
670 * @throws IOException could be caused by {@link #flush()}.
671 */
672 public final void setStream(final T stream, final boolean writeMode) throws IllegalArgumentException, IOException {
673 if( null != m_bytes && this.m_writeMode ) {
674 flush();
675 }
676 this.m_bytes.setStream(stream);
677 this.m_writeMode = writeMode;
678 resetLocal();
679 validateMode();
680 }
681
682 /**
683 * Sets the output-mode of this stream.
684 *
685 * If the previous stream was in {@link #canWrite() output mode},
686 * {@link #flush()} is being called.
687 *
688 * @throws IllegalArgumentException if requested <i>outputMode</i> doesn't match stream's {@link #canInput()} and {@link #canWrite()}.
689 * @throws IOException could be caused by {@link #flush()}.
690 */
691 public final void setWriteMode(final boolean writeMode) throws IllegalArgumentException, IOException {
692 if( this.m_writeMode ) {
693 flush();
694 }
695 this.m_writeMode = writeMode;
696 resetLocal();
697 validateMode();
698 }
699
700 /** Returns the currently used {@link ByteStream}. */
701 public final ByteStream<T> getStream() { return m_bytes; }
702
703 /** Returns the currently used {@link ByteStream}'s {@link ByteStream#getStream()}. */
704 public final T getSubStream() { return m_bytes.getStream(); }
705
706 /**
707 * Closing the underlying stream, implies {@link #flush()}.
708 * <p>
709 * Implementation will <code>null</code> the stream references,
710 * hence {@link #setStream(Object)} must be called before re-using instance.
711 * </p>
712 * <p>
713 * If the closed stream was in {@link #canWrite() output mode},
714 * {@link #flush()} is being called.
715 * </p>
716 *
717 * @throws IOException
718 */
719 public final void close() throws IOException {
720 if( null != m_bytes && this.m_writeMode ) {
721 flush();
722 }
723 m_bytes.close();
724 m_bytes = null;
725 resetLocal();
726 }
727
728 /**
729 * Synchronizes all underlying {@link ByteStream#canOutput() output stream} operations, or do nothing.
730 * <p>
731 * Method also flushes incomplete bytes to the underlying {@link ByteStream}
732 * and hence skips to the next byte position.
733 * </p>
734 * @return {@link #EOS} caused by writing, otherwise zero.
735 * @throws IllegalStateException if not in output mode or stream closed
736 * @throws IOException
737 */
738 public final int flush() throws IllegalStateException, IOException {
739 if( !m_writeMode || null == m_bytes ) {
740 throw new IllegalStateException("not in output-mode: "+this);
741 }
742 m_bytes.flush();
743 if( 0 != m_bitCount ) {
744 final int r = m_bytes.write((byte)m_bitCache);
745 m_bitCache = 0;
746 m_bitCount = 0;
747 if( EOS == r ) {
748 if( m_throwIOExceptionOnEOF ) {
749 throw new IOException("EOS "+this);
750 }
751 return EOS;
752 }
753 }
754 return 0;
755 }
756
757 /** Returns true in case stream is in write mode, false if in read mode. */
758 public final boolean canWrite() { return m_writeMode; }
759
760 /**
761 * Set {@code markpos} to current position, allowing the stream to be {@link #reset()}.
762 * @param readlimit maximum number of bytes able to read before invalidating the {@code markpos}.
763 * @throws IllegalStateException if not in input mode or stream closed
764 */
765 public final void mark(final int readLimit) throws IllegalStateException {
766 if( m_writeMode || null == m_bytes ) {
767 throw new IllegalStateException("not in input-mode: "+this);
768 }
769 m_bytes.mark(readLimit);
770 m_bitsDataMark = m_bitCache;
771 m_bitsCountMark = m_bitCount;
772 }
773
774 /**
775 * Reset stream position to <i>markpos</i> as set via {@link #mark(int)}.
776 * <p>
777 * <i>markpos</i> is kept, hence {@link #reset()} can be called multiple times.
778 * </p>
779 * @throws IllegalStateException if not in input mode or stream closed
780 * @throws IllegalStateException if <i>markpos</i> has not been set via {@link #mark(int)} or reset operation failed.
781 * @throws IOException if reset operation failed.
782 */
783 public final void reset() throws IllegalStateException, IOException {
784 if( m_writeMode || null == m_bytes ) {
785 throw new IllegalStateException("not in input-mode: "+this);
786 }
787 if( 0 > m_bitsCountMark ) {
788 throw new IllegalStateException("markpos not set: "+this);
789 }
790 m_bytes.reset();
791 m_bitCache = m_bitsDataMark;
792 m_bitCount = m_bitsCountMark;
793 }
794
795 /**
796 * Number of remaining bits cached to read before next byte-read (input mode)
797 * or number of remaining bits to be cached before next byte-write (output mode).
798 *
799 * Counting down from 7..0, starting with 0.
800 * - In input mode, zero indicates reading up next 8 bits and cont. w/ 7 bits.
801 * - In output mode, the cached bits are written when flipping over to 0.
802 */
803 public final int getCachedBitCount() { return m_bitCount; }
804
805 /**
806 * Return the last cached bit position read or written counting from [0..7].
807 * If no bit access has been performed, 7 is returned.
808 *
809 * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order.
810 */
811 public final int getLastCachedPos() { return 7 - m_bitCount; }
812
813 /**
814 * Return the next cached bit position to be read or written counting from [0..7].
815 *
816 * If no bit access has been performed or none is cached, 0 is returned.
817 *
818 * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order.
819 */
820 public final int getCachedPos() {
821 return 0 == m_bitCount ? 0 : 8 - m_bitCount;
822 }
823
824 /**
825 * Returns the current bit cache.
826 * @see #getCachedBitCount()
827 */
828 public final int getBitCache() { return m_bitCache; }
829
830 /**
831 * Returns the bit position in the stream.
832 */
833 public final long position() {
834 if( null == m_bytes ) {
835 return EOS;
836 } else if( 0 == m_bitCount ) {
837 return m_bytes.position() << 3;
838 } else {
839 final long bytePos = m_bytes.position() - ( m_writeMode ? 0 : 1 );
840 return ( bytePos << 3 ) + 8 - m_bitCount;
841 }
842 }
843
844 /**
845 * Sets this stream's bit position.
846 * <p>
847 * A set mark is cleared.
848 * </p>
849 * <p>
850 * Returns {@link Bitstream#EOS} is end-of-stream is reached,
851 * otherwise the new position.
852 * </p>
853 * <p>
854 * Known supporting implementation is {@link ByteBufferStream} and {@link ByteArrayStream}.
855 * </p>
856 *
857 * @param newPosition The new positive position.
858 *
859 * @return The new set position or {@link Bitstream#EOS} if end-of-stream is reached.
860 *
861 * @throws UnsupportedOperationException if not supported, i.e. {@link ByteInputStream} or {@link ByteOutputStream}
862 * @throws IllegalArgumentException If the {@code newPosition} is negative
863 * @throws IOException if read error occurs or EOS is reached and {@link #setThrowIOExceptionOnEOF(boolean)} is set to true.
864 * @throws IllegalStateException
865 */
866 public final long position(final long newPosition) throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException, IOException {
867 if( 0 > newPosition ) {
868 throw new IllegalArgumentException("new position not positive: "+newPosition);
869 }
870 final long pos0 = position();
871 final long diff = newPosition - pos0;
872 if( diff == 0 ) {
873 return newPosition;
874 } else if( diff > 0 ) {
875 if( diff > skip(diff) ) {
876 return EOS;
877 }
878 } else {
879 // backwards
880 if( m_writeMode ) {
881 if( 0 < m_bitCount && EOS == m_bytes.write((byte)m_bitCache) ) {
882 return 0;
883 }
884 }
885 resetLocal();
886 if( 0 != m_bytes.position(0) ) { // throws UnsupportedOperationException
887 return EOS;
888 }
889 if( newPosition > skip(newPosition) ) {
890 return EOS;
891 }
892 }
893 return newPosition;
894 }
895
896 /**
897 * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB.
898 * @return the read bit or {@link #EOS} if end-of-stream is reached.
899 * @throws IOException
900 * @throws IllegalStateException if not in input mode or stream closed
901 */
902 public final int readBit(final boolean msbFirst) throws UnsupportedOperationException, IllegalStateException, IOException {
903 if( m_writeMode || null == m_bytes ) {
904 throw new IllegalStateException("not in input-mode: "+this);
905 }
906 if ( 0 < m_bitCount ) {
907 --m_bitCount;
908 if( msbFirst ) {
909 return ( m_bitCache >>> m_bitCount ) & 0x01;
910 } else {
911 return ( m_bitCache >>> ( 7 - m_bitCount ) ) & 0x01;
912 }
913 } else {
914 m_bitCache = m_bytes.read();
915 if( EOS == m_bitCache ) {
916 if( m_throwIOExceptionOnEOF ) {
917 throw new IOException("EOS "+this);
918 }
919 return EOS;
920 } else {
921 m_bitCount=7;
922 if( msbFirst ) {
923 return m_bitCache >>> m_bitCount;
924 } else {
925 return m_bitCache & 0x01;
926 }
927 }
928 }
929 }
930
931 /**
932 * @param msbFirst if true outgoing stream bit order is MSB to LSB, otherwise LSB to MSB.
933 * @param bit
934 * @return true if successful, otherwise false
935 * @throws IOException
936 * @throws IllegalStateException if not in output mode or stream closed
937 */
938 public final boolean writeBit(final boolean msbFirst, final int bit) throws IllegalStateException, IOException {
939 if( !m_writeMode || null == m_bytes ) {
940 throw new IllegalStateException("not in output-mode: "+this);
941 }
942 if ( 0 < m_bitCount ) {
943 --m_bitCount;
944 if( msbFirst ) {
945 m_bitCache |= ( 0x01 & bit ) << m_bitCount;
946 } else {
947 m_bitCache |= ( 0x01 & bit ) << ( 7 - m_bitCount );
948 }
949 if( 0 == m_bitCount ) {
950 final int r = m_bytes.write((byte)m_bitCache);
951 if( m_throwIOExceptionOnEOF && EOS == r ) {
952 throw new IOException("EOS "+this);
953 }
954 return EOS != r;
955 }
956 } else {
957 m_bitCount = 7;
958 if( msbFirst ) {
959 m_bitCache = ( 0x01 & bit ) << m_bitCount;
960 } else {
961 m_bitCache = 0x01 & bit;
962 }
963 }
964 return true;
965 }
966
967 /**
968 * It is implementation dependent, whether backward skip giving a negative number is supported or not.
969 *
970 * @param n number of bits to skip
971 * @return actual skipped bits
972 * @throws IOException if read error occurs or EOS is reached and {@link #setThrowIOExceptionOnEOF(boolean)} is set to true.
973 * @throws IllegalStateException if closed
974 */
975 public long skip(final long n) throws IllegalStateException, IOException {
976 if( null == m_bytes ) {
977 throw new IllegalStateException("closed: "+this);
978 }
979 if( DEBUG ) {
980 System.err.println("Bitstream.skip.0: "+n+" - "+toStringImpl());
981 }
982 if( n > 0 ) {
983 if( n <= m_bitCount ) {
984 m_bitCount -= (int)n;
985 if( DEBUG ) {
986 System.err.println("Bitstream.skip.F_N1: "+n+" - "+toStringImpl());
987 }
988 return n;
989 } else { // n > bitCount
990 if( m_writeMode ) {
991 if( 0 < m_bitCount && EOS == m_bytes.write((byte)m_bitCache) ) {
992 return 0;
993 }
994 m_bitCache = 0;
995 }
996 final long n2 = n - m_bitCount; // subtract cached bits, bitsCount is zero at this point
997 final long n3 = n2 >>> 3; // bytes to skip
998 final long n4 = m_bytes.skip(n3); // actual skipped bytes
999 final int n5 = (int) ( n2 - ( n3 << 3 ) ); // remaining skip bits == nX % 8
1000 final long nX = ( n4 << 3 ) + n5 + m_bitCount; // actual skipped bits
1001 /**
1002 if( DEBUG ) {
1003 System.err.println("Bitstream.skip.1: n2 "+n2+", n3 "+n3+", n4 "+n4+", n5 "+n5+", nX "+nX+" - "+toStringImpl());
1004 } */
1005 m_bitCount = 0;
1006 if( nX < n ) {
1007 // couldn't complete skipping .. EOS .. etc
1008 m_bitCache = 0;
1009 if( DEBUG ) {
1010 System.err.println("Bitstream.skip.F_EOS: "+n+" - "+toStringImpl());
1011 }
1012 if( m_throwIOExceptionOnEOF ) {
1013 throw new IOException("EOS "+this);
1014 }
1015 return nX;
1016 }
1017 int notReadBits = 0;
1018 if( !m_writeMode && 0 < n5 ) {
1019 m_bitCache = m_bytes.read();
1020 if( EOS == m_bitCache ) {
1021 // notReadBits = n5;
1022 notReadBits = m_bitCount;
1023 } else {
1024 m_bitCount = 8 - n5;
1025 }
1026 } else {
1027 m_bitCount = ( 8 - n5 ) & 7; // % 8
1028 }
1029 if( DEBUG ) {
1030 System.err.println("Bitstream.skip.F_N2: "+n+", notReadBits "+notReadBits+" - "+toStringImpl());
1031 }
1032 return nX - notReadBits;
1033 }
1034 } else {
1035 // Zero skip or backward skip
1036 // FIXME: Backward skip n < 0
1037 return 0;
1038 }
1039 }
1040
1041 private static final boolean useFastPathStream = true;
1042 private static final boolean useFastPathTypes = true;
1043
1044 /**
1045 * Return incoming bits as read via {@link #readBit(boolean)} LSB-first as little-endian.
1046 * <p>
1047 * The incoming bit order is from low- to most-significant-bit, maintaining bit LSB-first order.
1048 * </p>
1049 * @param n number of bits, maximum 31 bits
1050 * @return the read bits from 0-n in the given order or {@link #EOS}.
1051 * @throws IllegalStateException if not in input mode or stream closed
1052 * @throws IllegalArgumentException if n > 31
1053 * @throws IOException
1054 */
1055 public int readBits31(final int n) throws IllegalArgumentException, IOException {
1056 if( 31 < n ) {
1057 throw new IllegalArgumentException("n > 31: "+n);
1058 }
1059 if( m_writeMode || null == m_bytes ) {
1060 throw new IllegalStateException("not in input-mode: "+this);
1061 }
1062 if( 0 == n ) {
1063 return 0;
1064 } else {
1065 if( !useFastPathStream ) {
1066 // Slow path
1067 int r = 0;
1068 for(int i=0; i < n; i++) {
1069 final int b = readBit(false /* msbFirst */);
1070 if( EOS == b ) {
1071 if( m_throwIOExceptionOnEOF ) {
1072 throw new IOException("EOS "+this);
1073 }
1074 return EOS;
1075 }
1076 r |= b << i;
1077 }
1078 return r;
1079 } else {
1080 // fast path
1081 int c = n;
1082 final int n1 = Math.min(n, m_bitCount); // remaining portion
1083 int r;
1084 if( 0 < n1 ) {
1085 final int m1 = ( 1 << n1 ) - 1;
1086 final int s1 = 7 - m_bitCount + 1; // LSBfirst: right-shift to new bits
1087 m_bitCount -= n1;
1088 c -= n1;
1089 // MSBfirst: r = ( m1 & ( bitBuffer >>> bitCount ) ) << c;
1090 r = ( m1 & ( m_bitCache >>> s1 ) ); // LSBfirst
1091 if( 0 == c ) {
1092 return r;
1093 }
1094 } else {
1095 r = 0;
1096 }
1097 assert( 0 == m_bitCount );
1098 int s = n1; // LSBfirst: left shift for additional elements
1099 do {
1100 m_bitCache = m_bytes.read();
1101 if( EOS == m_bitCache ) {
1102 if( m_throwIOExceptionOnEOF ) {
1103 throw new IOException("EOS "+this);
1104 }
1105 return EOS;
1106 }
1107 final int n2 = Math.min(c, 8); // full portion
1108 final int m2 = ( 1 << n2 ) - 1;
1109 m_bitCount = 8 - n2;
1110 c -= n2;
1111 // MSBfirst: r |= ( m2 & ( bitBuffer >>> bitCount ) ) << c;
1112 r |= ( m2 & m_bitCache ) << s; // LSBfirst on new bits
1113 s += n2;
1114 } while ( 0 < c );
1115 return r;
1116 }
1117 }
1118 }
1119
1120 /**
1121 * Write the given bits via {@link #writeBit(boolean, int)} LSB-first as little-endian.
1122 * <p>
1123 * The outgoing bit order is from low- to most-significant-bit, maintaining bit LSB-first order.
1124 * </p>
1125 * @param n number of bits, maximum 31 bits
1126 * @param bits the bits to write
1127 * @return the written bits or {@link #EOS}.
1128 * @throws IllegalStateException if not in output mode or stream closed
1129 * @throws IllegalArgumentException if n > 31
1130 * @throws IOException
1131 */
1132 public int writeBits31(final int n, final int bits) throws IllegalStateException, IllegalArgumentException, IOException {
1133 if( 31 < n ) {
1134 throw new IllegalArgumentException("n > 31: "+n);
1135 }
1136 if( !m_writeMode || null == m_bytes ) {
1137 throw new IllegalStateException("not in output-mode: "+this);
1138 }
1139 if( 0 < n ) {
1140 if( !useFastPathStream ) {
1141 // Slow path
1142 for(int i=0; i < n; i++) {
1143 if( !writeBit(false /* msbFirst */, ( bits >>> i ) & 0x1) ) {
1144 return EOS;
1145 }
1146 }
1147 } else {
1148 // fast path
1149 int c = n;
1150 final int n1 = Math.min(n, m_bitCount); // remaining portion
1151 if( 0 < n1 ) {
1152 final int m1 = ( 1 << n1 ) - 1;
1153 final int s1 = 7 - m_bitCount + 1; // LSBfirst: left-shift to free bit-pos
1154 m_bitCount -= n1;
1155 c -= n1;
1156 // MSBfirst: bitBuffer |= ( m1 & ( bits >>> c ) ) << bitCount;
1157 m_bitCache |= ( m1 & bits ) << s1 ; // LSBfirst
1158 if( 0 == m_bitCount ) {
1159 if( EOS == m_bytes.write((byte)m_bitCache) ) {
1160 if( m_throwIOExceptionOnEOF ) {
1161 throw new IOException("EOS "+this);
1162 }
1163 return EOS;
1164 }
1165 }
1166 if( 0 == c ) {
1167 return bits;
1168 }
1169 }
1170 assert( 0 == m_bitCount );
1171 int s = n1; // LSBfirst: left shift for additional elements
1172 do {
1173 final int n2 = Math.min(c, 8); // full portion
1174 final int m2 = ( 1 << n2 ) - 1;
1175 m_bitCount = 8 - n2;
1176 c -= n2;
1177 // MSBfirst: bitBuffer = ( m2 & ( bits >>> c ) ) << bitCount;
1178 m_bitCache = ( m2 & ( bits >>> s ) ); // LSBfirst
1179 s += n2;
1180 if( 0 == m_bitCount ) {
1181 if( EOS == m_bytes.write((byte)m_bitCache) ) {
1182 if( m_throwIOExceptionOnEOF ) {
1183 throw new IOException("EOS "+this);
1184 }
1185 return EOS;
1186 }
1187 }
1188 } while ( 0 < c );
1189 }
1190 }
1191 return bits;
1192 }
1193
1194 /**
1195 * Return incoming <code>uint8_t</code> as read via {@link #readBits31(int)}.
1196 * <p>
1197 * In case of a <code>int8_t</code> 2-complement signed value, simply cast the result to <code>byte</code>
1198 * after checking for {@link #EOS}.
1199 * </p>
1200 * @return {@link #EOS} or the 8bit unsigned value within the lower bits.
1201 * @throws IllegalStateException if not in input mode or stream closed
1202 * @throws IOException
1203 */
1204 public final int readUInt8() throws IllegalStateException, IOException {
1205 if( 0 == m_bitCount && useFastPathTypes ) {
1206 // fast path
1207 if( m_writeMode || null == m_bytes ) {
1208 throw new IllegalStateException("not in input-mode: "+this);
1209 }
1210 final int r = m_bytes.read();
1211 if( m_throwIOExceptionOnEOF && EOS == r ) {
1212 throw new IOException("EOS "+this);
1213 }
1214 return r;
1215 } else {
1216 return readBits31(8);
1217 }
1218 }
1219
1220 /**
1221 * Write the given 8 bits via {@link #writeBits31(int, int)}.
1222 * @return {@link #EOS} or the written 8bit value.
1223 * @throws IllegalStateException if not in output mode or stream closed
1224 * @throws IOException
1225 */
1226 public final int writeInt8(final byte int8) throws IllegalStateException, IOException {
1227 if( 0 == m_bitCount && useFastPathTypes ) {
1228 // fast path
1229 if( !m_writeMode || null == m_bytes ) {
1230 throw new IllegalStateException("not in output-mode: "+this);
1231 }
1232 final int r = m_bytes.write(int8);
1233 if( m_throwIOExceptionOnEOF && EOS == r ) {
1234 throw new IOException("EOS "+this);
1235 }
1236 return r;
1237 } else {
1238 return this.writeBits31(8, int8);
1239 }
1240 }
1241
1242 /**
1243 * Return incoming <code>uint16_t</code> as read via {@link #readBits31(int)} LSB-first as little-endian,
1244 * hence bytes are swapped if bigEndian.
1245 * <p>
1246 * In case of a <code>int16_t</code> 2-complement signed value, simply cast the result to <code>short</code>
1247 * after checking for {@link #EOS}.
1248 * </p>
1249 * @param bigEndian if true, swap incoming bytes to little-endian, otherwise leave them as little-endian.
1250 * @return {@link #EOS} or the 16bit unsigned value within the lower bits.
1251 * @throws IllegalStateException if not in input mode or stream closed
1252 * @throws IOException
1253 */
1254 public final int readUInt16(final boolean bigEndian) throws IllegalStateException, IOException {
1255 if( 0 == m_bitCount && useFastPathTypes ) {
1256 // fast path
1257 if( m_writeMode || null == m_bytes ) {
1258 throw new IllegalStateException("not in input-mode: "+this);
1259 }
1260 final int b1 = m_bytes.read();
1261 final int b2 = EOS != b1 ? m_bytes.read() : EOS;
1262 if( EOS == b2 ) {
1263 if( m_throwIOExceptionOnEOF ) {
1264 throw new IOException("EOS "+this);
1265 }
1266 return EOS;
1267 } else if( bigEndian ) {
1268 return b1 << 8 | b2;
1269 } else {
1270 return b2 << 8 | b1;
1271 }
1272 } else {
1273 final int i16 = readBits31(16);
1274 if( EOS == i16 ) {
1275 return EOS;
1276 } else if( bigEndian ) {
1277 final int b1 = 0xff & ( i16 >>> 8 );
1278 final int b2 = 0xff & i16;
1279 return b2 << 8 | b1;
1280 } else {
1281 return i16;
1282 }
1283 }
1284 }
1285
1286 /**
1287 * Return incoming <code>uint16_t</code> value and swap bytes according to bigEndian.
1288 * <p>
1289 * In case of a <code>int16_t</code> 2-complement signed value, simply cast the result to <code>short</code>.
1290 * </p>
1291 * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as big-endian.
1292 * @return the 16bit unsigned value within the lower bits.
1293 * @throws IndexOutOfBoundsException
1294 */
1295 public static final int readUInt16(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException {
1296 checkBounds(bytes, offset, 2);
1297 // Make sure we clear any high bits that get set in sign-extension
1298 final int b1 = bytes[offset] & 0xff;
1299 final int b2 = bytes[offset+1] & 0xff;
1300 if( bigEndian ) {
1301 return b1 << 8 | b2;
1302 } else {
1303 return b2 << 8 | b1;
1304 }
1305 }
1306
1307 /**
1308 * Write the given 16 bits via {@link #writeBits31(int, int)} LSB-first as little-endian,
1309 * hence bytes are swapped if bigEndian.
1310 * @param bigEndian if true, swap given bytes to little-endian, otherwise leave them as little-endian.
1311 * @return {@link #EOS} or the written 16bit value.
1312 * @throws IllegalStateException if not in output mode or stream closed
1313 * @throws IOException
1314 */
1315 public final int writeInt16(final boolean bigEndian, final short int16) throws IllegalStateException, IOException {
1316 if( 0 == m_bitCount && useFastPathTypes ) {
1317 // fast path
1318 if( !m_writeMode || null == m_bytes ) {
1319 throw new IllegalStateException("not in output-mode: "+this);
1320 }
1321 final byte hi = (byte) ( 0xff & ( int16 >>> 8 ) );
1322 final byte lo = (byte) ( 0xff & int16 );
1323 final byte b1, b2;
1324 if( bigEndian ) {
1325 b1 = hi;
1326 b2 = lo;
1327 } else {
1328 b1 = lo;
1329 b2 = hi;
1330 }
1331 if( EOS != m_bytes.write(b1) ) {
1332 if( EOS != m_bytes.write(b2) ) {
1333 return int16;
1334 }
1335 }
1336 if( m_throwIOExceptionOnEOF ) {
1337 throw new IOException("EOS "+this);
1338 }
1339 return EOS;
1340 } else if( bigEndian ) {
1341 final int b1 = 0xff & ( int16 >>> 8 );
1342 final int b2 = 0xff & int16;
1343 return writeBits31(16, b2 << 8 | b1);
1344 } else {
1345 return writeBits31(16, int16);
1346 }
1347 }
1348
1349 /**
1350 * Return incoming <code>uint32_t</code> as read via {@link #readBits31(int)} LSB-first as little-endian,
1351 * hence bytes are swapped if bigEndian.
1352 * <p>
1353 * In case of a <code>int32_t</code> 2-complement signed value, simply cast the result to <code>int</code>
1354 * after checking for {@link #EOS}.
1355 * </p>
1356 * @param bigEndian if true, swap incoming bytes to little-endian, otherwise leave them as little-endian.
1357 * @return {@link #EOS} or the 32bit unsigned value within the lower bits.
1358 * @throws IllegalStateException if not in input mode or stream closed
1359 * @throws IOException
1360 */
1361 public final long readUInt32(final boolean bigEndian) throws IllegalStateException, IOException {
1362 if( 0 == m_bitCount && useFastPathTypes ) {
1363 // fast path
1364 if( m_writeMode || null == m_bytes ) {
1365 throw new IllegalStateException("not in input-mode: "+this);
1366 }
1367 final int b1 = m_bytes.read();
1368 final int b2 = EOS != b1 ? m_bytes.read() : EOS;
1369 final int b3 = EOS != b2 ? m_bytes.read() : EOS;
1370 final int b4 = EOS != b3 ? m_bytes.read() : EOS;
1371 if( EOS == b4 ) {
1372 if( m_throwIOExceptionOnEOF ) {
1373 throw new IOException("EOS "+this);
1374 }
1375 return EOS;
1376 } else if( bigEndian ) {
1377 return 0xffffffffL & ( b1 << 24 | b2 << 16 | b3 << 8 | b4 );
1378 } else {
1379 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1380 }
1381 } else {
1382 final int i16a = readBits31(16);
1383 final int i16b = EOS != i16a ? readBits31(16) : EOS;
1384 if( EOS == i16b ) {
1385 return EOS;
1386 } else if( bigEndian ) {
1387 final int b1 = 0xff & ( i16b >>> 8 );
1388 final int b2 = 0xff & i16b;
1389 final int b3 = 0xff & ( i16a >>> 8 );
1390 final int b4 = 0xff & i16a;
1391 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1392 } else {
1393 return 0xffffffffL & ( i16b << 16 | i16a );
1394 }
1395 }
1396 }
1397
1398 /**
1399 * Return incoming <code>uint32_t</code> and swap bytes according to bigEndian.
1400 * <p>
1401 * In case of a <code>int32_t</code> 2-complement signed value, simply cast the result to <code>int</code>.
1402 * </p>
1403 * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as big-endian.
1404 * @return the 32bit unsigned value within the lower bits.
1405 * @throws IndexOutOfBoundsException
1406 */
1407 public static final long readUInt32(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException {
1408 checkBounds(bytes, offset, 4);
1409 final int b1 = bytes[offset];
1410 final int b2 = bytes[offset+1];
1411 final int b3 = bytes[offset+2];
1412 final int b4 = bytes[offset+3];
1413 if( bigEndian ) {
1414 return 0xffffffffL & ( b1 << 24 | b2 << 16 | b3 << 8 | b4 );
1415 } else {
1416 return 0xffffffffL & ( b4 << 24 | b3 << 16 | b2 << 8 | b1 );
1417 }
1418 }
1419
1420 /**
1421 * Write the given 32 bits via {@link #writeBits31(int, int)} LSB-first as little-endian,
1422 * hence bytes are swapped if bigEndian.
1423 * @param bigEndian if true, swap given bytes to little-endian, otherwise leave them as little-endian.
1424 * @return {@link #EOS} or the written 32bit value.
1425 * @throws IllegalStateException if not in output mode or stream closed
1426 * @throws IOException
1427 */
1428 public final int writeInt32(final boolean bigEndian, final int int32) throws IllegalStateException, IOException {
1429 if( 0 == m_bitCount && useFastPathTypes ) {
1430 // fast path
1431 if( !m_writeMode || null == m_bytes ) {
1432 throw new IllegalStateException("not in output-mode: "+this);
1433 }
1434 final byte p1 = (byte) ( 0xff & ( int32 >>> 24 ) );
1435 final byte p2 = (byte) ( 0xff & ( int32 >>> 16 ) );
1436 final byte p3 = (byte) ( 0xff & ( int32 >>> 8 ) );
1437 final byte p4 = (byte) ( 0xff & int32 );
1438 final byte b1, b2, b3, b4;
1439 if( bigEndian ) {
1440 b1 = p1;
1441 b2 = p2;
1442 b3 = p3;
1443 b4 = p4;
1444 } else {
1445 b1 = p4;
1446 b2 = p3;
1447 b3 = p2;
1448 b4 = p1;
1449 }
1450 if( EOS != m_bytes.write(b1) ) {
1451 if( EOS != m_bytes.write(b2) ) {
1452 if( EOS != m_bytes.write(b3) ) {
1453 if( EOS != m_bytes.write(b4) ) {
1454 return int32;
1455 }
1456 }
1457 }
1458 }
1459 if( m_throwIOExceptionOnEOF ) {
1460 throw new IOException("EOS "+this);
1461 }
1462 return EOS;
1463 } else if( bigEndian ) {
1464 final int p1 = 0xff & ( int32 >>> 24 );
1465 final int p2 = 0xff & ( int32 >>> 16 );
1466 final int p3 = 0xff & ( int32 >>> 8 );
1467 final int p4 = 0xff & int32 ;
1468 if( EOS != writeBits31(16, p2 << 8 | p1) ) {
1469 if( EOS != writeBits31(16, p4 << 8 | p3) ) {
1470 return int32;
1471 }
1472 }
1473 return EOS;
1474 } else {
1475 final int hi = 0x0000ffff & ( int32 >>> 16 );
1476 final int lo = 0x0000ffff & int32 ;
1477 if( EOS != writeBits31(16, lo) ) {
1478 if( EOS != writeBits31(16, hi) ) {
1479 return int32;
1480 }
1481 }
1482 return EOS;
1483 }
1484 }
1485
1486 /**
1487 * Reinterpret the given <code>int32_t</code> value as <code>uint32_t</code>,
1488 * i.e. perform the following cast to <code>long</code>:
1489 * <pre>
1490 * final long l = 0xffffffffL & int32;
1491 * </pre>
1492 */
1493 public static final long toUInt32Long(final int int32) {
1494 return 0xffffffffL & int32;
1495 }
1496
1497 /**
1498 * Returns the reinterpreted given <code>int32_t</code> value
1499 * as <code>uint32_t</code> if &le; {@link Integer#MAX_VALUE}
1500 * as within an <code>int</code> storage.
1501 * Otherwise return -1.
1502 */
1503 public static final int toUInt32Int(final int int32) {
1504 return uint32LongToInt(toUInt32Long(int32));
1505 }
1506
1507 /**
1508 * Returns the given <code>uint32_t</code> value <code>long</code>
1509 * value as <code>int</code> if &le; {@link Integer#MAX_VALUE}.
1510 * Otherwise return -1.
1511 */
1512 public static final int uint32LongToInt(final long uint32) {
1513 if( Integer.MAX_VALUE >= uint32 ) {
1514 return (int)uint32;
1515 } else {
1516 return -1;
1517 }
1518 }
1519
1520 @Override
1521 public String toString() {
1522 return String.format("Bitstream[%s]", toStringImpl());
1523 }
1524 protected String toStringImpl() {
1525 final String mode;
1526 final long bpos;
1527 if( null == m_bytes ) {
1528 mode = "closed";
1529 bpos = -1;
1530 } else {
1531 mode = m_writeMode ? "output" : "input";
1532 bpos = m_bytes.position();
1533 }
1534 return String.format((Locale)null, "%s, pos %d [byteP %d, bitCnt %d], bitbuf %s",
1535 mode, position(), bpos, m_bitCount, toHexBinString(true, m_bitCache, 8));
1536 }
1537
1538 private static final String strZeroPadding= "0000000000000000000000000000000000000000000000000000000000000000"; // 64
1539 public static String toBinString(final boolean msbFirst, final int v, final int bitCount) {
1540 if( 0 == bitCount ) {
1541 return "";
1542 }
1543 if( msbFirst ) {
1544 final int mask = (int) ( ( 1L << bitCount ) - 1L );
1545 final String s0 = Integer.toBinaryString( mask & v );
1546 return strZeroPadding.substring(0, bitCount-s0.length())+s0;
1547 } else {
1548 final char[] c = new char[32];
1549 for(int i=0; i<bitCount; i++) {
1550 c[i] = 0 != ( v & ( 1 << i ) ) ? '1' : '0';
1551 }
1552 final String s0 = new String(c, 0, bitCount);
1553 return s0+strZeroPadding.substring(0, bitCount-s0.length());
1554 }
1555 }
1556 public static String toHexBinString(final boolean msbFirst, final int v, final int bitCount) {
1557 final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4;
1558 return String.format((Locale)null, "[0x%0"+nibbles+"X, msbFirst %b, %s]", v, msbFirst, toBinString(msbFirst, v, bitCount));
1559 }
1560 public static final String toHexBinString(final boolean msbFirst, final byte[] data, final int offset, final int len) {
1561 final StringBuilder sb = new StringBuilder();
1562 sb.append("[");
1563 for(int i=0; i<len; i++) {
1564 final int v = 0xFF & data[offset+i];
1565 sb.append(toHexBinString(msbFirst, v, 8)).append(", ");
1566 }
1567 sb.append("]");
1568 return sb.toString();
1569 }
1570 public static final String toHexBinString(final boolean msbFirst, final ByteBuffer data, final int offset, final int len) {
1571 final StringBuilder sb = new StringBuilder();
1572 sb.append("[");
1573 for(int i=0; i<len; i++) {
1574 final int v = 0xFF & data.get(offset+i);
1575 sb.append(toHexBinString(msbFirst, v, 8)).append(", ");
1576 }
1577 sb.append("]");
1578 return sb.toString();
1579 }
1580
1581 public static void checkBounds(final byte[] sb, final int offset, final int remaining) throws IndexOutOfBoundsException {
1582 if( offset + remaining > sb.length ) {
1583 throw new IndexOutOfBoundsException("Buffer of size "+sb.length+" cannot hold offset "+offset+" + remaining "+remaining);
1584 }
1585 }
1586}
long position(final long newPosition)
void setStream(final byte[] stream)
ByteArrayStream(final byte[] stream)
void mark(final int readLimit)
void setStream(final ByteBuffer stream)
void mark(final int readLimit)
ByteBufferStream(final ByteBuffer stream)
long position(final long newPosition)
void mark(final int readLimit)
void setStream(final InputStream stream)
ByteInputStream(final InputStream stream)
long position(final long newPosition)
void setStream(final OutputStream stream)
void mark(final int readLimit)
long position(final long newPosition)
ByteOutputStream(final OutputStream stream)
int readBits31(final int n)
Return incoming bits as read via readBit(boolean) LSB-first as little-endian.
final int getCachedBitCount()
Number of remaining bits cached to read before next byte-read (input mode) or number of remaining bit...
final void mark(final int readLimit)
Set markpos to current position, allowing the stream to be reset().
final ByteStream< T > getStream()
Returns the currently used ByteStream.
final int flush()
Synchronizes all underlying output stream operations, or do nothing.
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 void setStream(final T stream, final boolean writeMode)
Sets the underlying stream, without close()ing the previous one.
final void reset()
Reset stream position to markpos as set via mark(int).
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...
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.
final int getCachedPos()
Return the next cached bit position to be read or written counting from [0..7].
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).
final void setWriteMode(final boolean writeMode)
Sets the output-mode of this stream.
long skip(final long n)
It is implementation dependent, whether backward skip giving a negative number is supported or not.
final long position(final long newPosition)
Sets this stream's bit position.
final boolean canWrite()
Returns true in case stream is in write mode, false if in read mode.
final void setThrowIOExceptionOnEOF(final boolean enable)
Enables or disables throwing an IOException in case EOS appears.
final int readBit(final boolean msbFirst)
Bitstream(final ByteStream< T > stream, final boolean outputMode)
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 int getLastCachedPos()
Return the last cached bit position read or written counting from [0..7].
final void close()
Closing the underlying stream, implies flush().
final boolean writeBit(final boolean msbFirst, final int bit)
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().
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.
final int getBitCache()
Returns the current bit cache.
static void checkBounds(final byte[] sb, final int offset, final int remaining)
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
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.