diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/java/com/jogamp/common/util/Bitstream.java | 1341 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/BitstreamData.java | 119 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestBitstream00.java | 115 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestBitstream01.java | 342 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestBitstream02.java | 133 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestBitstream03.java | 154 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestBitstream04.java | 158 |
7 files changed, 2362 insertions, 0 deletions
diff --git a/src/java/com/jogamp/common/util/Bitstream.java b/src/java/com/jogamp/common/util/Bitstream.java new file mode 100644 index 0000000..7bf0c16 --- /dev/null +++ b/src/java/com/jogamp/common/util/Bitstream.java @@ -0,0 +1,1341 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import jogamp.common.Debug; + +/** + * Versatile Bitstream implementation supporting: + * <ul> + * <li>Utilize I/O operations on I/O streams, buffers and arrays</li> + * <li>Consider MSBfirst / LSBfirst mode</li> + * <li>Linear bit R/W operations</li> + * <li>Bulk R/W operations w/ endian related type conversion</li> + * <li>Allow mark/reset and switching streams and input/output mode</li> + * <li>Optimized operations</li> + * </ul> + */ +public class Bitstream<T> { + private static final boolean DEBUG = Debug.debug("Bitstream"); + + /** End of stream marker, {@value} or 0xFFFFFFFF */ + public static final int EOS = -1; + + /** + * General byte stream. + */ + public static interface ByteStream<T> { + /** Sets the underlying stream, without {@link #close()}ing the previous one. */ + void setStream(final T stream); + + /** Returns the underlying stream */ + T getStream(); + + /** + * Closing the underlying stream, implies {@link #flush()}. + * <p> + * Implementation will <code>null</code> the stream references, + * hence {@link #setStream(Object)} must be called before re-using instance. + * </p> + * @throws IOException + */ + void close() throws IOException; + + /** + * Synchronizes all underlying {@link #canOutput() output stream} operations, or do nothing. + * @throws IOException + */ + void flush() throws IOException; + + /** Return true if stream can handle input, i.e. {@link #read()}. */ + boolean canInput(); + + /** Return true if stream can handle output, i.e. {@link #write(byte)} */ + boolean canOutput(); + + /** + * Returns the byte position in the stream. + */ + long position(); + + /** + * It is implementation dependent, whether backward skip giving a negative number is supported or not. + * @param n number of bytes to skip + * @return actual skipped bytes + * @throws IOException + */ + long skip(final long n) throws IOException; + + /** + * Set <i>markpos</i> to current position, allowing the stream to be {@link #reset()}. + * @param readLimit + * @throws UnsupportedOperationException is not supported, i.e. if stream is not an {@link #canInput() input stream}. + */ + void mark(final int readLimit) throws UnsupportedOperationException; + + /** + * Reset stream position to <i>markpos</i> as set via {@link #mark(int)}. + * <p> + * <i>markpos</i> is kept, hence {@link #reset()} can be called multiple times. + * </p> + * @throws UnsupportedOperationException is not supported, i.e. if stream is not an {@link #canInput() input stream}. + * @throws IllegalStateException if <i>markpos</i> has not been set via {@link #mark(int)} or reset operation failed. + * @throws IOException if reset operation failed. + */ + void reset() throws UnsupportedOperationException, IllegalStateException, IOException; + + /** + * Reads one byte from the stream. + * <p> + * Returns {@link Bitstream#EOS} is end-of-stream is reached, + * otherwise the resulting value. + * </p> + * @throws IOException + * @throws UnsupportedOperationException is not supported, i.e. if stream is not an {@link #canInput() input stream}. + */ + int read() throws UnsupportedOperationException, IOException; + + /** + * Writes one byte, to the stream. + * <p> + * Returns {@link Bitstream#EOS} is end-of-stream is reached, + * otherwise the written value. + * </p> + * @throws IOException + * @throws UnsupportedOperationException is not supported, i.e. if stream is not an {@link #canOutput() output stream}. + */ + int write(final byte val) throws UnsupportedOperationException, IOException; + } + + /** + * Specific {@link ByteStream byte stream}. + * <p> + * Can handle {@link #canInput() input} and {@link #canOutput() output} operations. + * </p> + */ + public static class ByteArrayStream implements ByteStream<byte[]> { + private byte[] media; + private int pos; + private int posMark; + + public ByteArrayStream(final byte[] stream) { + setStream(stream); + } + + @Override + public void setStream(final byte[] stream) { + media = stream; + pos = 0; + posMark = -1; + } + + @Override + public byte[] getStream() { return media; } + + @Override + public void close() { + media = null; + } + @Override + public void flush() { + // NOP + } + + @Override + public boolean canInput() { return true; } + + @Override + public boolean canOutput() { return true; } + + @Override + public long position() { return pos; } + + @Override + public long skip(final long n) { + final long skip; + if( n >= 0 ) { + final int remaining = media.length - pos; + skip = Math.min(remaining, (int)n); + } else { + final int n2 = (int)n * -1; + skip = -1 * Math.min(pos, n2); + } + pos += skip; + return skip; + } + + @Override + public void mark(final int readLimit) { + posMark = pos; + } + + @Override + public void reset() throws IllegalStateException { + if( 0 > posMark ) { + throw new IllegalStateException("markpos not set"); + } + if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); } + pos = posMark; + } + + @Override + public int read() { + final int r; + if( media.length > pos ) { + r = 0xff & media[pos++]; + } else { + r = -1; // EOS + } + if( DEBUG ) { + if( EOS != r ) { + System.err.println("u8["+(pos-1)+"] -> "+toHexBinString(r, 8)); + } else { + System.err.println("u8["+(pos-0)+"] -> EOS"); + } + } + return r; + } + + @Override + public int write(final byte val) { + final int r; + if( media.length > pos ) { + media[pos++] = val; + r = 0xff & val; + } else { + r = -1; // EOS + } + if( DEBUG ) { + if( EOS != r ) { + System.err.println("u8["+(pos-1)+"] <- "+toHexBinString(r, 8)); + } else { + System.err.println("u8["+(pos-0)+"] <- EOS"); + } + } + return r; + } + } + + /** + * Specific {@link ByteStream byte stream}. + * <p> + * Can handle {@link #canInput() input} and {@link #canOutput() output} operations. + * </p> + */ + public static class ByteBufferStream implements ByteStream<ByteBuffer> { + private ByteBuffer media; + private int pos; + private int posMark; + + public ByteBufferStream(final ByteBuffer stream) { + setStream(stream); + } + + @Override + public void setStream(final ByteBuffer stream) { + media = stream; + pos = 0; + posMark = -1; + } + + @Override + public ByteBuffer getStream() { return media; } + + @Override + public void close() { + media = null; + } + @Override + public void flush() { + // NOP + } + + @Override + public boolean canInput() { return true; } + + @Override + public boolean canOutput() { return true; } + + @Override + public long position() { return pos; } + + @Override + public long skip(final long n) { + final long skip; + if( n >= 0 ) { + final int remaining = media.limit() - pos; + skip = Math.min(remaining, (int)n); + } else { + final int n2 = (int)n * -1; + skip = -1 * Math.min(pos, n2); + } + pos += skip; + return skip; + } + + @Override + public void mark(final int readLimit) { + posMark = pos; + } + + @Override + public void reset() throws IllegalStateException { + if( 0 > posMark ) { + throw new IllegalStateException("markpos not set"); + } + if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); } + media.position(posMark); + pos = posMark; + } + + @Override + public int read() { + final int r; + if( media.limit() > pos ) { + r = 0xff & media.get(pos++); + } else { + r = -1; // EOS + } + if( DEBUG ) { + if( EOS != r ) { + System.err.println("u8["+(pos-1)+"] -> "+toHexBinString(r, 8)); + } else { + System.err.println("u8["+(pos-0)+"] -> EOS"); + } + } + return r; + } + + @Override + public int write(final byte val) { + final int r; + if( media.limit() > pos ) { + media.put(pos++, val); + r = 0xff & val; + } else { + r = -1; // EOS + } + if( DEBUG ) { + if( EOS != r ) { + System.err.println("u8["+(pos-1)+"] <- "+toHexBinString(r, 8)); + } else { + System.err.println("u8["+(pos-0)+"] <- EOS"); + } + } + return r; + } + } + + /** + * Specific {@link ByteStream byte stream}. + * <p> + * Can handle {@link #canInput() input} operations only. + * </p> + */ + public static class ByteInputStream implements ByteStream<InputStream> { + private BufferedInputStream media; + private long pos; + private long posMark; + + public ByteInputStream(final InputStream stream) { + setStream(stream); + } + + @Override + public void setStream(final InputStream stream) { + if( stream instanceof BufferedInputStream ) { + media = (BufferedInputStream) stream; + } else if( null != stream ) { + media = new BufferedInputStream(stream); + } else { + media = null; + } + pos = 0; + posMark = -1; + } + + @Override + public InputStream getStream() { return media; } + + @Override + public void close() throws IOException { + if( null != media ) { + media.close(); + media = null; + } + } + @Override + public void flush() { + // NOP + } + + @Override + public boolean canInput() { return true; } + + @Override + public boolean canOutput() { return false; } + + @Override + public long position() { return pos; } + + @Override + public long skip(final long n) throws IOException { + final long skip = media.skip(n); + pos += skip; + return skip; + } + + @Override + public void mark(final int readLimit) { + media.mark(readLimit); + posMark = pos; + } + + @Override + public void reset() throws IllegalStateException, IOException { + if( 0 > posMark ) { + throw new IllegalStateException("markpos not set"); + } + if(DEBUG) { System.err.println("rewind: "+pos+" -> "+posMark); } + media.reset(); + pos = posMark; + } + + @Override + public int read() throws IOException { + final int r = media.read(); + if(DEBUG) { + if( EOS != r ) { + System.err.println("u8["+pos+"] -> "+toHexBinString(r, 8)); + } else { + System.err.println("u8["+pos+"] -> EOS"); + } + } + if( EOS != r ) { + pos++; + } + return r; + } + + @Override + public int write(final byte val) throws UnsupportedOperationException { + throw new UnsupportedOperationException("not allowed with input stream"); + } + } + + /** + * Specific {@link ByteStream byte stream}. + * <p> + * Can handle {@link #canOutput() output} operations only. + * </p> + */ + public static class ByteOutputStream implements ByteStream<OutputStream> { + private BufferedOutputStream media; + private long pos = 0; + + public ByteOutputStream(final OutputStream stream) { + setStream(stream); + } + + @Override + public void setStream(final OutputStream stream) { + if( stream instanceof BufferedOutputStream ) { + media = (BufferedOutputStream) stream; + } else if( null != stream ) { + media = new BufferedOutputStream(stream); + } else { + media = null; + } + pos = 0; + } + + @Override + public void close() throws IOException { + if( null != media ) { + media.close(); + media = null; + } + } + @Override + public void flush() throws IOException { + if( null != media ) { + media.flush(); + } + } + + @Override + public boolean canInput() { return false; } + + @Override + public boolean canOutput() { return true; } + + @Override + public long position() { return pos; } + + @Override + public long skip(final long n) throws IOException { + long i = n; + while(i > 0) { + media.write(0); + i--; + } + final long skip = n-i; // should be n + pos += skip; + return skip; + } + + @Override + public OutputStream getStream() { return media; } + + @Override + public void mark(final int readLimit) throws UnsupportedOperationException { + throw new UnsupportedOperationException("not allowed with output stream"); + } + + @Override + public void reset() throws UnsupportedOperationException { + throw new UnsupportedOperationException("not allowed with output stream"); + } + + @Override + public int read() throws UnsupportedOperationException { + throw new UnsupportedOperationException("not allowed with output stream"); + } + + @Override + public int write(final byte val) throws IOException { + final int r = 0xff & val; + media.write(r); + if(DEBUG) { + System.err.println("u8["+pos+"] <- "+toHexBinString(r, 8)); + } + pos++; + return r; + } + } + + private ByteStream<T> bytes; + /** 8-bit cache of byte stream */ + private int bitBuffer; + private int bitsDataMark; + + /** See {@link #getBitCount()}. */ + private int bitCount; + private int bitsCountMark; + + private boolean outputMode; + + /** + * @param stream + * @param outputMode + * @throws IllegalArgumentException if requested <i>outputMode</i> doesn't match stream's {@link #canInput()} and {@link #canOutput()}. + */ + public Bitstream(final ByteStream<T> stream, final boolean outputMode) throws IllegalArgumentException { + this.bytes = stream; + this.outputMode = outputMode; + resetLocal(); + validateMode(); + } + + private final void resetLocal() { + bitBuffer = 0; + bitCount = 0; + bitsDataMark = 0; + bitsCountMark = -1; + } + private final void validateMode() throws IllegalArgumentException { + if( !canInput() && !canOutput() ) { + throw new IllegalArgumentException("stream can neither input nor output: "+this); + } + if( outputMode && !canOutput() ) { + throw new IllegalArgumentException("stream cannot output as requested: "+this); + } + if( !outputMode && !canInput() ) { + throw new IllegalArgumentException("stream cannot input as requested: "+this); + } + } + + /** + * Sets the underlying stream, without {@link #close()}ing the previous one. + * <p> + * If the previous stream was in {@link #canOutput() output mode}, + * {@link #flush()} is being called. + * </p> + * @throws IllegalArgumentException if requested <i>outputMode</i> doesn't match stream's {@link #canInput()} and {@link #canOutput()}. + * @throws IOException could be caused by {@link #flush()}. + */ + public final void setStream(final T stream, final boolean outputMode) throws IllegalArgumentException, IOException { + if( null != bytes && this.outputMode ) { + flush(); + } + this.bytes.setStream(stream); + this.outputMode = outputMode; + resetLocal(); + validateMode(); + } + + /** Returns the currently used {@link ByteStream}. */ + public final ByteStream<T> getStream() { return bytes; } + + /** Returns the currently used {@link ByteStream}'s {@link ByteStream#getStream()}. */ + public final T getSubStream() { return bytes.getStream(); } + + /** + * Closing the underlying stream, implies {@link #flush()}. + * <p> + * Implementation will <code>null</code> the stream references, + * hence {@link #setStream(Object)} must be called before re-using instance. + * </p> + * <p> + * If the closed stream was in {@link #canOutput() output mode}, + * {@link #flush()} is being called. + * </p> + * + * @throws IOException + */ + public final void close() throws IOException { + if( null != bytes && this.outputMode ) { + flush(); + } + bytes.close(); + bytes = null; + resetLocal(); + } + + /** + * Synchronizes all underlying {@link ByteStream#canOutput() output stream} operations, or do nothing. + * <p> + * Method also flushes incomplete bytes to the underlying {@link ByteStream} + * and hence skips to the next byte position. + * </p> + * @throws IllegalStateException if not in output mode or stream closed + * @throws IOException + */ + public final void flush() throws IllegalStateException, IOException { + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + bytes.flush(); + if( 0 != bitCount ) { + bytes.write((byte)bitBuffer); + bitBuffer = 0; + bitCount = 0; + } + } + + /** Return true if stream can handle input, i.e. {@link #readBit(boolean)}. */ + public final boolean canInput() { return null != bytes ? bytes.canInput() : false; } + + /** Return true if stream can handle output, i.e. {@link #writeBit(boolean, int)}. */ + public final boolean canOutput() { return null != bytes ? bytes.canOutput() : false; } + + /** + * Set <i>markpos</i> to current position, allowing the stream to be {@link #reset()}. + * @param readLimit + * @throws IllegalStateException if not in input mode or stream closed + */ + public final void mark(final int readLimit) throws IllegalStateException { + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + bytes.mark(readLimit); + bitsDataMark = bitBuffer; + bitsCountMark = bitCount; + } + + /** + * Reset stream position to <i>markpos</i> as set via {@link #mark(int)}. + * <p> + * <i>markpos</i> is kept, hence {@link #reset()} can be called multiple times. + * </p> + * @throws IllegalStateException if not in input mode or stream closed + * @throws IllegalStateException if <i>markpos</i> has not been set via {@link #mark(int)} or reset operation failed. + * @throws IOException if reset operation failed. + */ + public final void reset() throws IllegalStateException, IOException { + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + if( 0 > bitsCountMark ) { + throw new IllegalStateException("markpos not set: "+this); + } + bytes.reset(); + bitBuffer = bitsDataMark; + bitCount = bitsCountMark; + } + + /** + * Number of remaining bits in cache to read before next byte-read (input mode) + * or number of remaining bits to be cached before next byte-write (output mode). + * <p> + * Counting down from 7..0 7..0, starting with 0. + * </p> + * <p> + * In input mode, zero indicates reading a new byte and cont. w/ 7. + * In output mode, the cached byte is written when flipping over to 0. + * </p> + */ + public final int getBitCount() { return bitCount; } + + /** + * Return the last bit number read or written counting from [0..7]. + * If no bit access has been performed, 7 is returned. + * <p> + * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order. + * </p> + */ + public final int getLastBitPos() { return 7 - bitCount; } + + /** + * Return the next bit number to be read or write counting from [0..7]. + * If no bit access has been performed, 0 is returned. + * <p> + * Returned value is normalized [0..7], i.e. independent from <i>msb</i> or <i>lsb</i> read order. + * </p> + */ + public final int getBitPosition() { + if( 0 == bitCount ) { + return 0; + } else { + return 8 - bitCount; + } + } + + /** + * Returns the current bit buffer. + * @see #getBitCount() + */ + public final int getBitBuffer() { return bitBuffer; } + + /** + * Returns the bit position in the stream. + */ + public final long position() { + // final long bytePos = bytes.position() - ( !outputMode && 0 != bitCount ? 1 : 0 ); + // return ( bytePos << 3 ) + getBitPosition(); + if( null == bytes ) { + return EOS; + } else if( 0 == bitCount ) { + return bytes.position() << 3; + } else { + final long bytePos = bytes.position() - ( outputMode ? 0 : 1 ); + return ( bytePos << 3 ) + 8 - bitCount; + } + } + + /** + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @return the read bit or {@link #EOS} if end-of-stream is reached. + * @throws IOException + * @throws IllegalStateException if not in input mode or stream closed + */ + public final int readBit(final boolean msbFirst) throws IllegalStateException, IOException { + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + if( msbFirst ) { + // MSB + if ( 0 < bitCount ) { + bitCount--; + return ( bitBuffer >>> bitCount ) & 0x01; + } else { + bitBuffer = bytes.read(); + if( EOS == bitBuffer ) { + return EOS; + } else { + bitCount=7; + return bitBuffer >>> 7; + } + } + } else { + // LSB + if ( 0 < bitCount ) { + bitCount--; + return ( bitBuffer >>> ( 7 - bitCount ) ) & 0x01; + } else { + bitBuffer = bytes.read(); + if( EOS == bitBuffer ) { + return EOS; + } else { + bitCount=7; + return bitBuffer & 0x01; + } + } + } + } + + /** + * @param msbFirst if true outgoing stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param bit + * @return the currently written byte or {@link #EOS} if end-of-stream is reached. + * @throws IOException + * @throws IllegalStateException if not in output mode or stream closed + */ + public final int writeBit(final boolean msbFirst, final int bit) throws IllegalStateException, IOException { + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + if( msbFirst ) { + // MSB + if ( 0 < bitCount ) { + bitCount--; + bitBuffer |= ( 0x01 & bit ) << bitCount; + if( 0 == bitCount ) { + return bytes.write((byte)bitBuffer); + } + } else { + bitCount = 7; + bitBuffer = ( 0x01 & bit ) << 7; + } + } else { + // LSB + if ( 0 < bitCount ) { + bitCount--; + bitBuffer |= ( 0x01 & bit ) << ( 7 - bitCount ); + if( 0 == bitCount ) { + return bytes.write((byte)bitBuffer); + } + } else { + bitCount = 7; + bitBuffer = 0x01 & bit; + } + } + return bitBuffer; + } + + /** + * It is implementation dependent, whether backward skip giving a negative number is supported or not. + * + * @param n number of bits to skip + * @return actual skipped bits + * @throws IOException + * @throws IllegalStateException if closed + */ + public long skip(final long n) throws IllegalStateException, IOException { + if( null == bytes ) { + throw new IllegalStateException("closed: "+this); + } + if( DEBUG ) { + System.err.println("Bitstream.skip.0: "+n+" - "+toStringImpl()); + } + if( n > 0 ) { + if( n <= bitCount ) { + bitCount -= (int)n; + if( DEBUG ) { + System.err.println("Bitstream.skip.F_N1: "+n+" - "+toStringImpl()); + } + return n; + } else { // n > bitCount + if( outputMode ) { + if( 0 < bitCount ) { + bytes.write((byte)bitBuffer); + } + bitBuffer = 0; + } + final long n2 = n - bitCount; // subtract cached bits, bitsCount is zero at this point + final long n3 = n2 >>> 3; // bytes to skip + final long n4 = bytes.skip(n3); // actual skipped bytes + final int n5 = (int) ( n2 - ( n3 << 3 ) ); // remaining skip bits == nX % 8 + final long nX = ( n4 << 3 ) + n5 + bitCount; // actual skipped bits + /** + if( DEBUG ) { + System.err.println("Bitstream.skip.1: n2 "+n2+", n3 "+n3+", n4 "+n4+", n5 "+n5+", nX "+nX+" - "+toStringImpl()); + } */ + if( nX < n ) { + // couldn't complete skipping .. EOS .. etc + bitCount = 0; + bitBuffer = 0; + if( DEBUG ) { + System.err.println("Bitstream.skip.F_EOS: "+n+" - "+toStringImpl()); + } + return nX; + } + bitCount = ( 8 - n5 ) & 7; // % 8 + if( !outputMode && 0 < bitCount ) { + bitBuffer = bytes.read(); + } + if( DEBUG ) { + System.err.println("Bitstream.skip.F_N2: "+n+" - "+toStringImpl()); + } + return nX; + } + } else { + // FIXME: Backward skip + return 0; + } + } + + /** + * Return incoming bits as read via {@link #readBit(boolean)}. + * <p> + * The incoming bits are stored in MSB-first order, i.e. first on highest position and last bit on lowest position. + * Hence reading w/ <i>lsbFirst</i>, the bit order will be reversed! + * </p> + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param n number of bits, maximum 31 bits + * @return the read bits from 0-n in the given order or {@link #EOS}. + * @throws IllegalStateException if not in input mode or stream closed + * @throws IllegalArgumentException if n > 31 + * @throws IOException + */ + public int readBits31(final boolean msbFirst, final int n) throws IllegalArgumentException, IOException { + if( 31 < n ) { + throw new IllegalArgumentException("n > 31: "+n); + } + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + if( !msbFirst || 0 == n ) { + // Slow path + int r = 0; + int c = n; + while(--c >= 0) { + final int b = readBit(msbFirst); + if( EOS == b ) { + return EOS; + } + r |= b << c; + } + return r; + } else { + // fast path: MSB + int c = n; + final int n1 = Math.min(c, bitCount); // remaining portion + int r; + if( 0 < n1 ) { + final int m1 = ( 1 << n1 ) - 1; + bitCount -= n1; + c -= n1; + r = ( m1 & ( bitBuffer >>> bitCount ) ) << c; + if( 0 == c ) { + return r; + } + } else { + r = 0; + } + assert( 0 == bitCount ); + do { + bitBuffer = bytes.read(); + if( EOS == bitBuffer ) { + return EOS; + } + final int n2 = Math.min(c, 8); // full portion + final int m2 = ( 1 << n2 ) - 1; + bitCount = 8 - n2; + c -= n2; + r |= ( m2 & ( bitBuffer >>> bitCount ) ) << c; + } while ( 0 < c ); + return r; + } + } + + /** + * Write the given bits via {@link #writeBit(boolean, int)}. + * <p> + * The given bits are scanned from LSB-first order. + * Hence reading w/ <i>msbFirst</i>, the bit order will be reversed! + * </p> + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param n number of bits, maximum 31 bits + * @param bits the bits to write + * @return the written bits or {@link #EOS}. + * @throws IllegalStateException if not in output mode or stream closed + * @throws IllegalArgumentException if n > 31 + * @throws IOException + */ + public int writeBits31(final boolean msbFirst, final int n, final int bits) throws IllegalStateException, IllegalArgumentException, IOException { + if( 31 < n ) { + throw new IllegalArgumentException("n > 31: "+n); + } + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + if( !msbFirst || 0 == n ) { + // Slow path + int c = n; + while(--c >= 0) { + final int b = writeBit(msbFirst, ( bits >>> c ) & 0x1); + if( EOS == b ) { + return EOS; + } + } + } else { + // fast path: MSB + int c = n; + final int n1 = Math.min(c, bitCount); // remaining portion + if( 0 < n1 ) { + final int m1 = ( 1 << n1 ) - 1; + bitCount -= n1; + c -= n1; + bitBuffer |= ( m1 & ( bits >> c ) ) << bitCount; + if( 0 == bitCount ) { + if( EOS == bytes.write((byte)bitBuffer) ) { + return EOS; + } + } + if( 0 == c ) { + return bits; + } + } + assert( 0 == bitCount ); + do { + final int n2 = Math.min(c, 8); // full portion + final int m2 = ( 1 << n2 ) - 1; + bitCount = 8 - n2; + c -= n2; + bitBuffer = ( m2 & ( bits >> c ) ) << bitCount; + if( 0 == bitCount ) { + if( EOS == bytes.write((byte)bitBuffer) ) { + return EOS; + } + } + } while ( 0 < c ); + } + return bits; + } + + /** + * Return incoming int8 as read via {@link #readBits31(boolean, int)}. + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @return {@link #EOS} or the 8bit value, which might be unsigned or 2-complement signed value. + * In the signed case, user shall cast the result to <code>byte</code>. + * @throws IllegalStateException if not in input mode or stream closed + * @throws IOException + */ + public final int readInt8(final boolean msbFirst) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + return bytes.read(); + } else { + return readBits31(msbFirst, 8); + } + } + + /** + * Write the given int8 via {@link #writeBits31(boolean, int, int)}. + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @return {@link #EOS} or the written 8bit value. + * @throws IllegalStateException if not in output mode or stream closed + * @throws IOException + */ + public final int writeInt8(final boolean msbFirst, final byte int8) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + return bytes.write(int8); + } else { + return this.writeBits31(msbFirst, 8, int8); + } + } + + /** + * Return incoming int16 as read via {@link #readBits31(boolean, int)} + * and swap bytes if !bigEndian. + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as little-endian. + * @return {@link #EOS} or the 16bit value, which might be unsigned or 2-complement signed value. + * In the signed case, user shall cast the result to <code>short</code>. + * @throws IllegalStateException if not in input mode or stream closed + * @throws IOException + */ + public final int readInt16(final boolean msbFirst, final boolean bigEndian) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + final int b1 = bytes.read(); + final int b2 = EOS != b1 ? bytes.read() : EOS; + if( EOS == b2 ) { + return EOS; + } else if( bigEndian ) { + return b1 << 8 | b2; + } else { + return b2 << 8 | b1; + } + } else { + final int i16 = readBits31(msbFirst, 16); + if( EOS == i16 ) { + return EOS; + } else if( bigEndian ) { + return i16; + } else { + final int b1 = 0xff & ( i16 >>> 8 ); + final int b2 = 0xff & i16; + return b2 << 8 | b1; + } + } + } + + /** + * Return incoming int16 value and swap bytes if !bigEndian. + * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as little-endian. + * @return the 16bit value, which might be unsigned or 2-complement signed value. + * In the signed case, user shall cast the result to <code>short</code>. + * @throws IndexOutOfBoundsException + */ + public static final int readInt16(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException { + checkBounds(bytes, offset, 2); + final int b1 = bytes[offset]; + final int b2 = bytes[offset+1]; + if( bigEndian ) { + return b1 << 8 | b2; + } else { + return b2 << 8 | b1; + } + } + + /** + * Write the given int16 via {@link #writeBits31(boolean, int, int)}, + * while swapping bytes if !bigEndian beforehand. + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param bigEndian if false, swap given bytes to little-endian, otherwise leave them as little-endian. + * @return {@link #EOS} or the written 16bit value. + * @throws IllegalStateException if not in output mode or stream closed + * @throws IOException + */ + public final int writeInt16(final boolean msbFirst, final boolean bigEndian, final short int16) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + final byte hi = (byte) ( 0xff & ( int16 >>> 8 ) ); + final byte lo = (byte) ( 0xff & int16 ); + final byte b1, b2; + if( bigEndian ) { + b1 = hi; + b2 = lo; + } else { + b1 = lo; + b2 = hi; + } + if( EOS != bytes.write(b1) ) { + if( EOS != bytes.write(b2) ) { + return int16; + } + } + return EOS; + } else if( bigEndian ) { + return writeBits31(msbFirst, 16, int16); + } else { + final int b1 = 0xff & ( int16 >>> 8 ); + final int b2 = 0xff & int16; + return writeBits31(msbFirst, 16, b2 << 8 | b1); + } + } + + /** + * Return incoming int32 as read via {@link #readBits31(boolean, int)} + * and swap bytes if !bigEndian. + * <p> + * In case the returned value shall be interpreted as unsigned, + * it shall be cast to <code>long</code> as follows: + * <pre> + * final long l = 0xffffffffL & int32; + * </pre> + * </p> + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as little-endian. + * @return {@link #EOS} or the 32bit value, which might be unsigned or 2-complement signed value. + * @throws IllegalStateException if not in input mode or stream closed + * @throws IOException + */ + public final int readInt32(final boolean msbFirst, final boolean bigEndian) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( outputMode || null == bytes ) { + throw new IllegalStateException("not in input-mode: "+this); + } + final int b1 = bytes.read(); + final int b2 = EOS != b1 ? bytes.read() : EOS; + final int b3 = EOS != b2 ? bytes.read() : EOS; + final int b4 = EOS != b3 ? bytes.read() : EOS; + if( EOS == b4 ) { + return EOS; + } else if( bigEndian ) { + return b1 << 24 | b2 << 16 | b3 << 8 | b4; + } else { + return b4 << 24 | b3 << 16 | b2 << 8 | b1; + } + } else { + final int i16a = readBits31(msbFirst, 16); + final int i16b = EOS != i16a ? readBits31(msbFirst, 16) : EOS; + if( EOS == i16b ) { + return EOS; + } else if( bigEndian ) { + return i16a << 16 | i16b; + } else { + final int b1 = 0xff & ( i16a >>> 8 ); + final int b2 = 0xff & i16a; + final int b3 = 0xff & ( i16b >>> 8 ); + final int b4 = 0xff & i16b; + return b4 << 24 | b3 << 16 | b2 << 8 | b1; + } + } + } + + /** + * Return incoming int32 as read via {@link #readBits31(boolean, int)} + * and swap bytes if !bigEndian. + * <p> + * In case the returned value shall be interpreted as unsigned, + * it shall be cast to <code>long</code> as follows: + * <pre> + * final long l = 0xffffffffL & int32; + * </pre> + * </p> + * @param bigEndian if false, swap incoming bytes to little-endian, otherwise leave them as little-endian. + * @return the 32bit value, which might be unsigned or 2-complement signed value. + * @throws IndexOutOfBoundsException + */ + public static final int readInt32(final boolean bigEndian, final byte[] bytes, final int offset) throws IndexOutOfBoundsException { + checkBounds(bytes, offset, 4); + final int b1 = bytes[offset]; + final int b2 = bytes[offset+1]; + final int b3 = bytes[offset+2]; + final int b4 = bytes[offset+3]; + if( bigEndian ) { + return b1 << 24 | b2 << 16 | b3 << 8 | b4; + } else { + return b4 << 24 | b3 << 16 | b2 << 8 | b1; + } + } + + /** + * Write the given int32 via {@link #writeBits31(boolean, int, int)}, + * while swapping bytes if !bigEndian beforehand. + * @param msbFirst if true incoming stream bit order is MSB to LSB, otherwise LSB to MSB. + * @param bigEndian if false, swap given bytes to little-endian, otherwise leave them as little-endian. + * @return {@link #EOS} or the written 32bit value. + * @throws IllegalStateException if not in output mode or stream closed + * @throws IOException + */ + public final int writeInt32(final boolean msbFirst, final boolean bigEndian, final int int32) throws IllegalStateException, IOException { + if( 0 == bitCount && msbFirst ) { + // fast path + if( !outputMode || null == bytes ) { + throw new IllegalStateException("not in output-mode: "+this); + } + final byte p1 = (byte) ( 0xff & ( int32 >>> 24 ) ); + final byte p2 = (byte) ( 0xff & ( int32 >>> 16 ) ); + final byte p3 = (byte) ( 0xff & ( int32 >>> 8 ) ); + final byte p4 = (byte) ( 0xff & int32 ); + final byte b1, b2, b3, b4; + if( bigEndian ) { + b1 = p1; + b2 = p2; + b3 = p3; + b4 = p4; + } else { + b1 = p4; + b2 = p3; + b3 = p2; + b4 = p1; + } + if( EOS != bytes.write(b1) ) { + if( EOS != bytes.write(b2) ) { + if( EOS != bytes.write(b3) ) { + if( EOS != bytes.write(b4) ) { + return int32; + } + } + } + } + return EOS; + } else if( bigEndian ) { + final int hi = 0x0000ffff & ( int32 >>> 16 ); + final int lo = 0x0000ffff & int32 ; + if( EOS != writeBits31(msbFirst, 16, hi) ) { + if( EOS != writeBits31(msbFirst, 16, lo) ) { + return int32; + } + } + return EOS; + } else { + final int p1 = 0xff & ( int32 >>> 24 ); + final int p2 = 0xff & ( int32 >>> 16 ); + final int p3 = 0xff & ( int32 >>> 8 ); + final int p4 = 0xff & int32 ; + if( EOS != writeBits31(msbFirst, 16, p4 << 8 | p3) ) { + if( EOS != writeBits31(msbFirst, 16, p2 << 8 | p1) ) { + return int32; + } + } + return EOS; + } + } + + public String toString() { + return String.format("Bitstream[%s]", toStringImpl()); + } + protected String toStringImpl() { + final String mode; + final long bpos; + if( null == bytes ) { + mode = "closed"; + bpos = -1; + } else { + mode = outputMode ? "output" : "input"; + bpos = bytes.position(); + } + return String.format("%s, pos %d [byteP %d, bitCnt %d], bitbuf %s", + mode, position(), bpos, bitCount, toHexBinString(bitBuffer, 8)); + } + + private static final String strZeroPadding= "0000000000000000000000000000000000000000000000000000000000000000"; // 64 + public static String toBinString(final int v, final int bitCount) { + if( 0 == bitCount ) { + return ""; + } + final int mask = (int) ( ( 1L << bitCount ) - 1L ); + final String s0 = Integer.toBinaryString( mask & v ); + return strZeroPadding.substring(0, bitCount-s0.length())+s0; + } + public static String toHexBinString(final int v, final int bitCount) { + final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4; + return String.format("[%0"+nibbles+"X, %s]", v, toBinString(v, bitCount)); + } + public static void checkBounds(final byte[] sb, final int offset, final int remaining) throws IndexOutOfBoundsException { + if( offset + remaining > sb.length ) { + throw new IndexOutOfBoundsException("Buffer of size "+sb.length+" cannot hold offset "+offset+" + remaining "+remaining); + } + } +} diff --git a/src/junit/com/jogamp/common/util/BitstreamData.java b/src/junit/com/jogamp/common/util/BitstreamData.java new file mode 100644 index 0000000..93e8ad0 --- /dev/null +++ b/src/junit/com/jogamp/common/util/BitstreamData.java @@ -0,0 +1,119 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.nio.ByteBuffer; + +public class BitstreamData { + // + // MSB -> LSB over whole data + // + public static final byte[] testBytesMSB = new byte[] { (byte)0xde, (byte)0xaf, (byte)0xca, (byte)0xfe }; + public static final String[] testStringsMSB = new String[] { "11011110", "10101111", "11001010", "11111110" }; + public static final String testStringMSB = testStringsMSB[0]+testStringsMSB[1]+testStringsMSB[2]+testStringsMSB[3]; + + // + // MSB -> LSB, reverse bit-order over each byte of testBytesLSB + // + public static final byte[] testBytesMSB_rev = new byte[] { (byte)0xfe, (byte)0xca, (byte)0xaf, (byte)0xde }; + public static final String[] testStringsMSB_rev = new String[] { "11111110", "11001010", "10101111", "11011110" }; + public static final String testStringMSB_rev = testStringsMSB_rev[0]+testStringsMSB_rev[1]+testStringsMSB_rev[2]+testStringsMSB_rev[3]; + + // + // LSB -> MSB over whole data + // + public static final byte[] testBytesLSB = new byte[] { (byte)0x7f, (byte)0x53, (byte)0xf5, (byte)0x7b }; + public static final String[] testStringsLSB = new String[] { "01111111", "01010011", "11110101", "01111011" }; + public static final String testStringLSB = testStringsLSB[0]+testStringsLSB[1]+testStringsLSB[2]+testStringsLSB[3]; + + // + // LSB -> MSB, reverse bit-order over each byte of testBytesMSB + // + public static final byte[] testBytesLSB_revByte = new byte[] { (byte)0x7b, (byte)0xf5, (byte)0x53, (byte)0x7f }; + public static final String[] testStringsLSB_revByte = new String[] { "01111011", "11110101", "01010011", "01111111" }; + public static final String testStringLSB_revByte = testStringsLSB_revByte[0]+testStringsLSB_revByte[1]+testStringsLSB_revByte[2]+testStringsLSB_revByte[3]; + + public static final void dumpData(String prefix, byte[] data, int offset, int len) { + for(int i=0; i<len; ) { + System.err.printf("%s: %03d: ", prefix, i); + for(int j=0; j<8 && i<len; j++, i++) { + final int v = 0xFF & data[offset+i]; + System.err.printf(toHexBinaryString(v, 8)+", "); + } + System.err.println(""); + } + } + public static final void dumpData(String prefix, ByteBuffer data, int offset, int len) { + for(int i=0; i<len; ) { + System.err.printf("%s: %03d: ", prefix, i); + for(int j=0; j<8 && i<len; j++, i++) { + final int v = 0xFF & data.get(offset+i); + System.err.printf(toHexBinaryString(v, 8)+", "); + } + System.err.println(""); + } + } + + public static String toHexString(int v) { + return "0x"+Integer.toHexString(v); + } + public static final String strZeroPadding= "0000000000000000000000000000000000000000000000000000000000000000"; // 64 + public static String toBinaryString(int v, int bitCount) { + if( 0 == bitCount ) { + return ""; + } + final int mask = (int) ( ( 1L << bitCount ) - 1L ); + final String s0 = Integer.toBinaryString( mask & v ); + return strZeroPadding.substring(0, bitCount-s0.length())+s0; + } + public static String toBinaryString(long v, int bitCount) { + if( 0 == bitCount ) { + return ""; + } + final long mask = ( 1L << bitCount ) - 1L; + final String s0 = Long.toBinaryString( mask & v ); + return strZeroPadding.substring(0, bitCount-s0.length())+s0; + } + public static String toHexBinaryString(long v, int bitCount) { + final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4; + return String.format("[%0"+nibbles+"X, %s]", v, toBinaryString(v, bitCount)); + } + public static String toHexBinaryString(int v, int bitCount) { + final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4; + return String.format("[%0"+nibbles+"X, %s]", v, toBinaryString(v, bitCount)); + } + public static String toHexBinaryString(short v, int bitCount) { + final int nibbles = 0 == bitCount ? 2 : ( bitCount + 3 ) / 4; + return String.format("[%0"+nibbles+"X, %s]", v, toBinaryString(v, bitCount)); + } + public static String toUnsignedBinaryString(final int int32) { + final long l = 0xffffffffL & int32; + return l+", "+toHexBinaryString(l, 32); + } +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream00.java b/src/junit/com/jogamp/common/util/TestBitstream00.java new file mode 100644 index 0000000..ef1fd45 --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitstream00.java @@ -0,0 +1,115 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.LongBuffer; + +import org.junit.Test; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; +import static com.jogamp.common.util.BitstreamData.*; +import com.jogamp.junit.util.JunitTracer; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test basic bit operations for {@link Bitstream} + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitstream00 extends JunitTracer { + + @Test + public void test00ShowByteOrder() { + int i_ff = 0xff; + byte b_ff = (byte)i_ff; + System.err.println("i_ff "+i_ff+", "+toHexBinaryString(i_ff, 8)); + System.err.println("b_ff "+b_ff+", "+toHexBinaryString(0xff & b_ff, 8)); + + System.err.println("Platform.LITTLE_ENDIAN: "+Platform.isLittleEndian()); + showOrderImpl(null); + showOrderImpl(ByteOrder.BIG_ENDIAN); + showOrderImpl(ByteOrder.LITTLE_ENDIAN); + + dumpData("tstMSB.whole", testBytesMSB, 0, testBytesMSB.length); + dumpData("tstLSB.pbyte", testBytesLSB_revByte, 0, testBytesLSB_revByte.length); + dumpData("tstLSB.whole", testBytesLSB, 0, testBytesLSB.length); + } + void showOrderImpl(ByteOrder byteOrder) { + final ByteBuffer bb_long = ByteBuffer.allocate(Buffers.SIZEOF_LONG); + if( null != byteOrder ) { + bb_long.order(byteOrder); + } + System.err.println("Order: "+byteOrder+" -> "+bb_long.order()); + final LongBuffer lb = bb_long.asLongBuffer(); + lb.put(0, 0x0807060504030201L); + dumpData("long."+byteOrder, bb_long, 0, bb_long.capacity()); + + final ByteBuffer bb_int = ByteBuffer.allocate(Buffers.SIZEOF_INT); + if( null != byteOrder ) { + bb_int.order(byteOrder); + } + final IntBuffer ib = bb_int.asIntBuffer(); + ib.put(0, 0x04030201); + dumpData("long."+byteOrder, bb_int, 0, bb_int.capacity()); + + dumpData("tstMSB.whole", testBytesMSB, 0, testBytesMSB.length); + dumpData("tstLSB.pbyte", testBytesLSB_revByte, 0, testBytesLSB_revByte.length); + dumpData("tstLSB.whole", testBytesLSB, 0, testBytesLSB.length); + } + + @Test + public void test01ShiftSigned() { + shiftSigned(0xA0000000); // negative w/ '1010' top-nibble + shiftSigned(-1); + } + void shiftSigned(final int i0) { + System.err.printf("i0 %012d, %s%n", i0, toHexBinaryString(i0, 32)); + { + int im = i0; + for(int i=0; i<32; i++) { + final int bitA = ( 0 != ( i0 & ( 1 << i ) ) ) ? 1 : 0; + final int bitB = im & 0x01; + System.err.printf("[%02d]: bit[%d, %d], im %012d, %s%n", i, bitA, bitB, im, toHexBinaryString(im, 32)); + im = im >>> 1; + } + } + } + + public static void main(String args[]) throws IOException { + String tstname = TestBitstream00.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream01.java b/src/junit/com/jogamp/common/util/TestBitstream01.java new file mode 100644 index 0000000..a8ae4a5 --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitstream01.java @@ -0,0 +1,342 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.junit.util.JunitTracer; +import static com.jogamp.common.util.BitstreamData.*; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test {@link Bitstream} w/ raw linear and bulk read/write access w/o semantics: + * <ul> + * <li>{@link Bitstream#readBit(boolean)}</li> + * <li>{@link Bitstream#writeBit(boolean, int)}</li> + * <li>{@link Bitstream#mark(int)}</li> + * <li>{@link Bitstream#reset()}</li> + * <li>{@link Bitstream#flush()}</li> + * <li>{@link Bitstream#readBits31(boolean, int)}</li> + * <li>{@link Bitstream#writeBits31(boolean, int, int)}</li> + * </ul> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitstream01 extends JunitTracer { + + Bitstream<ByteBuffer> getTestStream(final boolean msbFirst, int preBits, final int skipBits, final int postBits) throws IOException { + final int byteCount = ( preBits + skipBits + postBits + 7 ) / 8; + final ByteBuffer bbTest = ByteBuffer.allocate(byteCount); + final Bitstream.ByteBufferStream bbsTest = new Bitstream.ByteBufferStream(bbTest); + final Bitstream<ByteBuffer> bsTest = new Bitstream<ByteBuffer>(bbsTest, true /* outputMode */); + final String sTest0; + if( msbFirst ) { + sTest0 = testStringMSB.substring(0, preBits+skipBits+postBits); + } else { + sTest0 = testStringLSB.substring(0, preBits+skipBits+postBits); + } + for(int i=0; i<preBits+skipBits+postBits; i++) { + final int bit = Integer.valueOf(sTest0.substring(i, i+1)); + bsTest.writeBit(msbFirst, bit); + } + Assert.assertEquals(preBits+skipBits+postBits, bsTest.position()); + bsTest.setStream(bsTest.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + return bsTest; + } + + String getTestStreamResultAsString(final boolean msbFirst, int preBits, final int skipBits, final int postBits) { + final String pre, post; + if( msbFirst ) { + pre = testStringMSB.substring(0, preBits); + post = testStringMSB.substring(preBits+skipBits, preBits+skipBits+postBits); + } else { + pre = testStringLSB.substring(0, preBits); + post = testStringLSB.substring(preBits+skipBits, preBits+skipBits+postBits); + } + final String r = pre + post; + System.err.println("Test: <"+pre+"> + <"+post+"> = <"+r+">"); + return r; + } + + @Test + public void test01LinearBitsMSBFirst() throws IOException { + testLinearBitsImpl(true /* msbFirst */); + } + @Test + public void test02LinearBitsLSBFirst() throws IOException { + testLinearBitsImpl(false /* msbFirst */); + } + void testLinearBitsImpl(final boolean msbFirst) throws IOException { + testLinearBitsImpl(msbFirst, 0, 0, 1); + testLinearBitsImpl(msbFirst, 0, 0, 3); + testLinearBitsImpl(msbFirst, 0, 0, 8); + testLinearBitsImpl(msbFirst, 0, 0, 10); + testLinearBitsImpl(msbFirst, 0, 0, 30); + testLinearBitsImpl(msbFirst, 0, 0, 32); + + testLinearBitsImpl(msbFirst, 3, 0, 3); + testLinearBitsImpl(msbFirst, 8, 0, 3); + testLinearBitsImpl(msbFirst, 9, 0, 3); + + testLinearBitsImpl(msbFirst, 0, 1, 1); + testLinearBitsImpl(msbFirst, 0, 1, 3); + testLinearBitsImpl(msbFirst, 0, 2, 8); + testLinearBitsImpl(msbFirst, 0, 8, 10); + testLinearBitsImpl(msbFirst, 0, 12, 20); + testLinearBitsImpl(msbFirst, 0, 23, 9); + + testLinearBitsImpl(msbFirst, 1, 1, 1); + testLinearBitsImpl(msbFirst, 2, 1, 3); + testLinearBitsImpl(msbFirst, 7, 2, 8); + testLinearBitsImpl(msbFirst, 8, 8, 8); + testLinearBitsImpl(msbFirst, 15, 12, 5); + testLinearBitsImpl(msbFirst, 16, 11, 5); + } + + String readBits(final boolean msbFirst, final Bitstream<?> copy, final Bitstream<?> input, final int preCount, final int count) throws IOException { + final StringBuilder sbRead = new StringBuilder(); + int i = 0; + while( i < count ) { + final int bit = input.readBit(msbFirst); + if( Bitstream.EOS == bit ) { + System.err.printf(" EOS"); + break; + } else { + sbRead.append( ( 0 != bit ) ? '1' : '0' ); + i++; + Assert.assertEquals(i+preCount, input.position()); + if( null != copy ) { + copy.writeBit(msbFirst, bit); + Assert.assertEquals(i+preCount, copy.position()); + } + } + } + Assert.assertEquals(i+preCount, input.position()); + if( null != copy ) { + Assert.assertEquals(i+preCount, copy.position()); + } + return sbRead.toString(); + } + + void testLinearBitsImpl(final boolean msbFirst, int preBits, int skipBits, final int postBits) throws IOException { + final int totalBits = preBits+skipBits+postBits; + System.err.println("XXX TestLinearBits: msbFirst "+msbFirst+", preBits "+preBits+", skipBits "+skipBits+", postBits "+postBits+", totalBits "+totalBits); + + // prepare bitstream + System.err.println("Prepare bitstream"); + final Bitstream<ByteBuffer> bsTest = getTestStream(msbFirst, preBits, skipBits, postBits); + dumpData("Test", bsTest.getSubStream(), 0, bsTest.getSubStream().limit()); + final String sTest = getTestStreamResultAsString(msbFirst, preBits, skipBits, postBits); + + // init copy-bitstream + final int byteCount = ( totalBits + 7 ) / 8; + final ByteBuffer bbCopy = ByteBuffer.allocate(byteCount); + final Bitstream.ByteBufferStream bbsCopy = new Bitstream.ByteBufferStream(bbCopy); + final Bitstream<ByteBuffer> bsCopy = new Bitstream<ByteBuffer>(bbsCopy, true /* outputMode */); + + // read-bitstream .. and copy bits while reading + System.err.println("Reading bitstream: "+bsTest); + { + final String sReadPre = readBits(msbFirst, bsCopy, bsTest, 0, preBits); + { + final int skippedBits = (int) bsTest.skip(skipBits); + Assert.assertEquals(skipBits, skippedBits); + } + { + final int skippedBits = (int) bsCopy.skip(skipBits); + Assert.assertEquals(skipBits, skippedBits); + } + final String sReadPost = readBits(msbFirst, bsCopy, bsTest, preBits+skipBits, postBits); + final String sRead = sReadPre + sReadPost; + System.err.println("Read.Test: <"+sReadPre+"> + <"+sReadPost+"> = <"+sRead+">"); + Assert.assertEquals(sTest, sRead); + Assert.assertEquals(totalBits, bsTest.position()); + Assert.assertEquals(totalBits, bsCopy.position()); + } + + // read copy .. + bsCopy.setStream(bsCopy.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + dumpData("Copy", bbCopy, 0, bbCopy.limit()); + System.err.println("Reading copy-bitstream: "+bsCopy); + bsCopy.mark(0); // mark at beginning + Assert.assertEquals(0, bsCopy.position()); + { + final String sReadPre1 = readBits(msbFirst, null, bsCopy, 0, preBits); + { + final int skippedBits = (int) bsCopy.skip(skipBits); + Assert.assertEquals(skipBits, skippedBits); + } + final String sReadPost1 = readBits(msbFirst, null, bsCopy, preBits+skipBits, postBits); + final String sRead1 = sReadPre1 + sReadPost1; + System.err.println("Read.Copy.1: <"+sReadPre1+"> + <"+sReadPost1+"> = <"+sRead1+">"); + Assert.assertEquals(sTest, sRead1); + + bsCopy.reset(); + final String sReadPre2 = readBits(msbFirst, null, bsCopy, 0, preBits); + Assert.assertEquals(sReadPre1, sReadPre2); + { + final int skippedBits = (int) bsCopy.skip(skipBits); + Assert.assertEquals(skipBits, skippedBits); + } + final String sReadPost2 = readBits(msbFirst, null, bsCopy, preBits+skipBits, postBits); + Assert.assertEquals(sReadPost1, sReadPost2); + final String sRead2 = sReadPre2 + sReadPost2; + System.err.println("Read.Copy.2: <"+sReadPre2+"> + <"+sReadPost2+"> = <"+sRead2+">"); + Assert.assertEquals(sTest, sRead2); + Assert.assertEquals(totalBits, bsCopy.position()); + } + } + + @Test + public void test03BulkBitsMSBFirst() throws IOException { + testBulkBitsImpl(true); + } + @Test + public void test04BulkBitsLSBFirst() throws IOException { + testBulkBitsImpl(false); + } + void testBulkBitsImpl(final boolean msbFirst) throws IOException { + testBulkBitsImpl(msbFirst, 0, 0, 1); + testBulkBitsImpl(msbFirst, 0, 0, 3); + testBulkBitsImpl(msbFirst, 0, 0, 8); + testBulkBitsImpl(msbFirst, 0, 0, 10); + testBulkBitsImpl(msbFirst, 0, 0, 30); + testBulkBitsImpl(msbFirst, 0, 0, 31); + + testBulkBitsImpl(msbFirst, 3, 0, 3); + testBulkBitsImpl(msbFirst, 8, 0, 3); + testBulkBitsImpl(msbFirst, 9, 0, 3); + + testBulkBitsImpl(msbFirst, 0, 1, 1); + testBulkBitsImpl(msbFirst, 0, 1, 3); + testBulkBitsImpl(msbFirst, 0, 2, 8); + testBulkBitsImpl(msbFirst, 0, 8, 10); + testBulkBitsImpl(msbFirst, 0, 12, 20); + testBulkBitsImpl(msbFirst, 0, 23, 9); + testBulkBitsImpl(msbFirst, 0, 1, 31); + + testBulkBitsImpl(msbFirst, 1, 1, 1); + testBulkBitsImpl(msbFirst, 2, 1, 3); + testBulkBitsImpl(msbFirst, 7, 2, 8); + testBulkBitsImpl(msbFirst, 8, 8, 8); + testBulkBitsImpl(msbFirst, 15, 12, 5); + testBulkBitsImpl(msbFirst, 16, 11, 5); + } + + void testBulkBitsImpl(final boolean msbFirst, int preBits, final int skipBits, final int postBits) throws IOException { + final int totalBits = preBits+skipBits+postBits; + System.err.println("XXX TestBulkBits: msbFirst "+msbFirst+", preBits "+preBits+", skipBits "+skipBits+", postBits "+postBits+", totalBits "+totalBits); + + // prepare bitstream + System.err.println("Prepare bitstream"); + final Bitstream<ByteBuffer> bsTest = getTestStream(msbFirst, preBits, skipBits, postBits); + dumpData("Test", bsTest.getSubStream(), 0, bsTest.getSubStream().limit()); + final String sTest = getTestStreamResultAsString(msbFirst, preBits, skipBits, postBits); + + // init copy-bitstream + final int byteCount = ( totalBits + 7 ) / 8; + final ByteBuffer bbCopy = ByteBuffer.allocate(byteCount); + final Bitstream.ByteBufferStream bbsCopy = new Bitstream.ByteBufferStream(bbCopy); + final Bitstream<ByteBuffer> bsCopy = new Bitstream<ByteBuffer>(bbsCopy, true /* outputMode */); + + // read-bitstream .. and copy bits while reading + System.err.println("Reading bitstream: "+bsTest); + { + final int readBitsPre = bsTest.readBits31(msbFirst, preBits); + Assert.assertEquals(readBitsPre, bsCopy.writeBits31(msbFirst, preBits, readBitsPre)); + + final int skippedReadBits = (int) bsTest.skip(skipBits); + final int skippedBitsCopy = (int) bsCopy.skip(skipBits); + + final int readBitsPost = bsTest.readBits31(msbFirst, postBits); + Assert.assertEquals(readBitsPost, bsCopy.writeBits31(msbFirst, postBits, readBitsPost)); + final String sReadPre = toBinaryString(readBitsPre, preBits); + final String sReadPost = toBinaryString(readBitsPost, postBits); + final String sRead = sReadPre + sReadPost; + System.err.println("Read.Test: <"+sReadPre+"> + <"+sReadPost+"> = <"+sRead+">"); + + Assert.assertEquals(skipBits, skippedReadBits); + Assert.assertEquals(sTest, sRead); + Assert.assertEquals(totalBits, bsTest.position()); + Assert.assertEquals(skipBits, skippedBitsCopy); + } + + // read copy .. + bsCopy.setStream(bsCopy.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + dumpData("Copy", bbCopy, 0, bbCopy.limit()); + System.err.println("Reading copy-bitstream: "+bsCopy); + Assert.assertEquals(0, bsCopy.position()); + { + final int copyBitsPre = bsCopy.readBits31(msbFirst, preBits); + + final int skippedCopyBits = (int) bsCopy.skip(skipBits); + + final int copyBitsPost = bsCopy.readBits31(msbFirst, postBits); + final String sCopyPre = toBinaryString(copyBitsPre, preBits); + final String sCopyPost = toBinaryString(copyBitsPost, postBits); + final String sCopy = sCopyPre + sCopyPost; + System.err.println("Copy.Test: <"+sCopyPre+"> + <"+sCopyPost+"> = <"+sCopy+">"); + + Assert.assertEquals(skipBits, skippedCopyBits); + Assert.assertEquals(sTest, sCopy); + Assert.assertEquals(totalBits, bsCopy.position()); + } + } + + @Test + public void test05ErrorHandling() throws IOException { + // prepare bitstream + final Bitstream<ByteBuffer> bsTest = getTestStream(false, 0, 0, 0); + System.err.println("01a: "+bsTest); + bsTest.close(); + System.err.println("01b: "+bsTest); + + try { + bsTest.readBit(false); + } catch (Exception e) { + Assert.assertNotNull(e); + } + try { + bsTest.writeBit(false, 1); + } catch (Exception e) { + Assert.assertNotNull(e); + } + } + + public static void main(String args[]) throws IOException { + String tstname = TestBitstream01.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream02.java b/src/junit/com/jogamp/common/util/TestBitstream02.java new file mode 100644 index 0000000..dc7333c --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitstream02.java @@ -0,0 +1,133 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.junit.util.JunitTracer; + +import static com.jogamp.common.util.BitstreamData.*; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test {@link Bitstream} w/ int8 read/write access w/ semantics + * as well as with aligned and unaligned access. + * <ul> + * <li>{@link Bitstream#readInt8(boolean, boolean)}</li> + * <li>{@link Bitstream#writeInt8(boolean, boolean, byte)}</li> + * </ul> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitstream02 extends JunitTracer { + + @Test + public void test01Int8BitsAligned() throws IOException { + test01Int8BitsAlignedImpl((byte)0); + test01Int8BitsAlignedImpl((byte)1); + test01Int8BitsAlignedImpl((byte)7); + test01Int8BitsAlignedImpl(Byte.MIN_VALUE); + test01Int8BitsAlignedImpl(Byte.MAX_VALUE); + test01Int8BitsAlignedImpl((byte)0xff); + } + void test01Int8BitsAlignedImpl(byte val8) throws IOException { + // Test with buffer defined value + final ByteBuffer bb = ByteBuffer.allocate(Buffers.SIZEOF_BYTE); + System.err.println("XXX Test01Int8BitsAligned: value "+val8+", "+toHexBinaryString(val8, 8)); + bb.put(0, val8); + + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, false /* outputMode */); + { + final byte r8 = (byte) bs.readInt8(true /* msbFirst */); + System.err.println("Read8.1 "+r8+", "+toHexBinaryString(r8, 8)); + Assert.assertEquals(val8, r8); + } + + // Test with written bitstream value + bs.setStream(bs.getSubStream(), true /* outputMode */); + bs.writeInt8(true /* msbFirst */, val8); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + { + final byte r8 = (byte) bs.readInt8(true /* msbFirst */); + System.err.println("Read8.2 "+r8+", "+toHexBinaryString(r8, 8)); + Assert.assertEquals(val8, r8); + } + } + + @Test + public void test02Int8BitsUnaligned() throws IOException { + test02Int8BitsUnalignedImpl(0); + test02Int8BitsUnalignedImpl(1); + test02Int8BitsUnalignedImpl(7); + test02Int8BitsUnalignedImpl(8); + test02Int8BitsUnalignedImpl(15); + test02Int8BitsUnalignedImpl(24); + test02Int8BitsUnalignedImpl(25); + } + void test02Int8BitsUnalignedImpl(final int preBits) throws IOException { + test02Int8BitsUnalignedImpl(preBits, (byte)0); + test02Int8BitsUnalignedImpl(preBits, (byte)1); + test02Int8BitsUnalignedImpl(preBits, (byte)7); + test02Int8BitsUnalignedImpl(preBits, Byte.MIN_VALUE); + test02Int8BitsUnalignedImpl(preBits, Byte.MAX_VALUE); + test02Int8BitsUnalignedImpl(preBits, (byte)0xff); + } + void test02Int8BitsUnalignedImpl(int preBits, byte val8) throws IOException { + final int preBytes = ( preBits + 7 ) >>> 3; + final int byteCount = preBytes + Buffers.SIZEOF_BYTE; + final ByteBuffer bb = ByteBuffer.allocate(byteCount); + System.err.println("XXX Test02Int8BitsUnaligned: preBits "+preBits+", value "+val8+", "+toHexBinaryString(val8, 8)); + + // Test with written bitstream value + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, true /* outputMode */); + bs.writeBits31(true /* msbFirst */, preBits, 0); + bs.writeInt8(true /* msbFirst */, val8); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + + final int rPre = (short) bs.readBits31(true /* msbFirst */, preBits); + final byte r8 = (byte) bs.readInt8(true /* msbFirst */); + System.err.println("ReadPre "+rPre+", "+toBinaryString(rPre, preBits)); + System.err.println("Read8 "+r8+", "+toHexBinaryString(r8, 8)); + Assert.assertEquals(val8, r8); + } + + public static void main(String args[]) throws IOException { + String tstname = TestBitstream02.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream03.java b/src/junit/com/jogamp/common/util/TestBitstream03.java new file mode 100644 index 0000000..7eb89e7 --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitstream03.java @@ -0,0 +1,154 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.junit.util.JunitTracer; + +import static com.jogamp.common.util.BitstreamData.*; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test {@link Bitstream} w/ int16 read/write access w/ semantics + * as well as with aligned and unaligned access. + * <ul> + * <li>{@link Bitstream#readInt16(boolean, boolean)}</li> + * <li>{@link Bitstream#writeInt16(boolean, boolean, short)}</li> + * </ul> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitstream03 extends JunitTracer { + + @Test + public void test01Int16BitsAligned() throws IOException { + test01Int16BitsImpl(null); + test01Int16BitsImpl(ByteOrder.BIG_ENDIAN); + test01Int16BitsImpl(ByteOrder.LITTLE_ENDIAN); + } + void test01Int16BitsImpl(ByteOrder byteOrder) throws IOException { + test01Int16BitsAlignedImpl(byteOrder, (short)0); + test01Int16BitsAlignedImpl(byteOrder, (short)1); + test01Int16BitsAlignedImpl(byteOrder, (short)7); + test01Int16BitsAlignedImpl(byteOrder, (short)0x0fff); + test01Int16BitsAlignedImpl(byteOrder, Short.MIN_VALUE); + test01Int16BitsAlignedImpl(byteOrder, Short.MAX_VALUE); + test01Int16BitsAlignedImpl(byteOrder, (short)0xffff); + } + void test01Int16BitsAlignedImpl(ByteOrder byteOrder, short val16) throws IOException { + // Test with buffer defined value + final ByteBuffer bb = ByteBuffer.allocate(Buffers.SIZEOF_SHORT); + if( null != byteOrder ) { + bb.order(byteOrder); + } + final boolean bigEndian = ByteOrder.BIG_ENDIAN == bb.order(); + System.err.println("XXX Test01Int16BitsAligned: byteOrder "+byteOrder+" (bigEndian "+bigEndian+"), value "+val16+", "+toHexBinaryString(val16, 16)); + bb.putShort(0, val16); + + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, false /* outputMode */); + { + final short r16 = (short) bs.readInt16(true /* msbFirst */, bigEndian); + System.err.println("Read16.1 "+r16+", "+toHexBinaryString(r16, 16)); + Assert.assertEquals(val16, r16); + } + + // Test with written bitstream value + bs.setStream(bs.getSubStream(), true /* outputMode */); + bs.writeInt16(true /* msbFirst */, bigEndian, val16); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + { + final short r16 = (short) bs.readInt16(true /* msbFirst */, bigEndian); + System.err.println("Read16.2 "+r16+", "+toHexBinaryString(r16, 16)); + Assert.assertEquals(val16, r16); + } + } + + @Test + public void test02Int16BitsUnaligned() throws IOException { + test02Int16BitsUnalignedImpl(null); + test02Int16BitsUnalignedImpl(ByteOrder.BIG_ENDIAN); + test02Int16BitsUnalignedImpl(ByteOrder.LITTLE_ENDIAN); + } + void test02Int16BitsUnalignedImpl(ByteOrder byteOrder) throws IOException { + test02Int16BitsUnalignedImpl(byteOrder, 0); + test02Int16BitsUnalignedImpl(byteOrder, 1); + test02Int16BitsUnalignedImpl(byteOrder, 7); + test02Int16BitsUnalignedImpl(byteOrder, 8); + test02Int16BitsUnalignedImpl(byteOrder, 15); + test02Int16BitsUnalignedImpl(byteOrder, 24); + test02Int16BitsUnalignedImpl(byteOrder, 25); + } + void test02Int16BitsUnalignedImpl(ByteOrder byteOrder, final int preBits) throws IOException { + test02Int16BitsUnalignedImpl(byteOrder, preBits, (short)0); + test02Int16BitsUnalignedImpl(byteOrder, preBits, (short)1); + test02Int16BitsUnalignedImpl(byteOrder, preBits, (short)7); + test02Int16BitsUnalignedImpl(byteOrder, preBits, (short)0x0fff); + test02Int16BitsUnalignedImpl(byteOrder, preBits, Short.MIN_VALUE); + test02Int16BitsUnalignedImpl(byteOrder, preBits, Short.MAX_VALUE); + test02Int16BitsUnalignedImpl(byteOrder, preBits, (short)0xffff); + } + void test02Int16BitsUnalignedImpl(ByteOrder byteOrder, int preBits, short val16) throws IOException { + final int preBytes = ( preBits + 7 ) >>> 3; + final int byteCount = preBytes + Buffers.SIZEOF_SHORT; + final ByteBuffer bb = ByteBuffer.allocate(byteCount); + if( null != byteOrder ) { + bb.order(byteOrder); + } + final boolean bigEndian = ByteOrder.BIG_ENDIAN == bb.order(); + System.err.println("XXX Test02Int16BitsUnaligned: byteOrder "+byteOrder+" (bigEndian "+bigEndian+"), preBits "+preBits+", value "+val16+", "+toHexBinaryString(val16, 16)); + + // Test with written bitstream value + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, true /* outputMode */); + bs.writeBits31(true /* msbFirst */, preBits, 0); + bs.writeInt16(true /* msbFirst */, bigEndian, val16); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + + final int rPre = (short) bs.readBits31(true /* msbFirst */, preBits); + final short r16 = (short) bs.readInt16(true /* msbFirst */, bigEndian); + System.err.println("ReadPre "+rPre+", "+toBinaryString(rPre, preBits)); + System.err.println("Read16 "+r16+", "+toHexBinaryString(r16, 16)); + Assert.assertEquals(val16, r16); + } + + public static void main(String args[]) throws IOException { + String tstname = TestBitstream03.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream04.java b/src/junit/com/jogamp/common/util/TestBitstream04.java new file mode 100644 index 0000000..dfc9c90 --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitstream04.java @@ -0,0 +1,158 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.junit.util.JunitTracer; + +import static com.jogamp.common.util.BitstreamData.*; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test {@link Bitstream} w/ int32 read/write access w/ semantics + * as well as with aligned and unaligned access. + * <ul> + * <li>{@link Bitstream#readInt32(boolean, boolean)}</li> + * <li>{@link Bitstream#writeInt32(boolean, boolean, int)}</li> + * </ul> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitstream04 extends JunitTracer { + + @Test + public void test01Int32BitsAligned() throws IOException { + test01Int32BitsImpl(null); + test01Int32BitsImpl(ByteOrder.BIG_ENDIAN); + test01Int32BitsImpl(ByteOrder.LITTLE_ENDIAN); + } + void test01Int32BitsImpl(ByteOrder byteOrder) throws IOException { + test01Int32BitsAlignedImpl(byteOrder, 0); + test01Int32BitsAlignedImpl(byteOrder, 1); + test01Int32BitsAlignedImpl(byteOrder, -1); + test01Int32BitsAlignedImpl(byteOrder, 7); + test01Int32BitsAlignedImpl(byteOrder, 0x0fffffff); + test01Int32BitsAlignedImpl(byteOrder, Integer.MIN_VALUE); + test01Int32BitsAlignedImpl(byteOrder, Integer.MAX_VALUE); + test01Int32BitsAlignedImpl(byteOrder, 0xffffffff); + } + void test01Int32BitsAlignedImpl(ByteOrder byteOrder, int val32) throws IOException { + // Test with buffer defined value + final ByteBuffer bb = ByteBuffer.allocate(Buffers.SIZEOF_INT); + if( null != byteOrder ) { + bb.order(byteOrder); + } + final boolean bigEndian = ByteOrder.BIG_ENDIAN == bb.order(); + System.err.println("XXX Test01Int32BitsAligned: byteOrder "+byteOrder+" (bigEndian "+bigEndian+"), value "+val32+", "+toHexBinaryString(val32, 32)); + System.err.println("XXX Test01Int32BitsAligned: "+toUnsignedBinaryString(val32)); + bb.putInt(0, val32); + + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, false /* outputMode */); + { + final int r32 = bs.readInt32(true /* msbFirst */, bigEndian); + System.err.println("Read32.1 "+r32+", "+toHexBinaryString(r32, 32)+", "+toUnsignedBinaryString(r32)); + Assert.assertEquals(val32, r32); + } + + // Test with written bitstream value + bs.setStream(bs.getSubStream(), true /* outputMode */); + bs.writeInt32(true /* msbFirst */, bigEndian, val32); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + { + final int r32 = bs.readInt32(true /* msbFirst */, bigEndian); + System.err.println("Read32.2 "+r32+", "+toHexBinaryString(r32, 32)+", "+toUnsignedBinaryString(r32)); + Assert.assertEquals(val32, r32); + } + } + + @Test + public void test02Int32BitsUnaligned() throws IOException { + test02Int32BitsUnalignedImpl(null); + test02Int32BitsUnalignedImpl(ByteOrder.BIG_ENDIAN); + test02Int32BitsUnalignedImpl(ByteOrder.LITTLE_ENDIAN); + } + void test02Int32BitsUnalignedImpl(ByteOrder byteOrder) throws IOException { + test02Int32BitsUnalignedImpl(byteOrder, 0); + test02Int32BitsUnalignedImpl(byteOrder, 1); + test02Int32BitsUnalignedImpl(byteOrder, 7); + test02Int32BitsUnalignedImpl(byteOrder, 8); + test02Int32BitsUnalignedImpl(byteOrder, 15); + test02Int32BitsUnalignedImpl(byteOrder, 24); + test02Int32BitsUnalignedImpl(byteOrder, 25); + } + void test02Int32BitsUnalignedImpl(ByteOrder byteOrder, final int preBits) throws IOException { + test02Int32BitsUnalignedImpl(byteOrder, preBits, 0); + test02Int32BitsUnalignedImpl(byteOrder, preBits, 1); + test02Int32BitsUnalignedImpl(byteOrder, preBits, -1); + test02Int32BitsUnalignedImpl(byteOrder, preBits, 7); + test02Int32BitsUnalignedImpl(byteOrder, preBits, 0x0fffffff); + test02Int32BitsUnalignedImpl(byteOrder, preBits, Integer.MIN_VALUE); + test02Int32BitsUnalignedImpl(byteOrder, preBits, Integer.MAX_VALUE); + test02Int32BitsUnalignedImpl(byteOrder, preBits, 0xffffffff); + } + void test02Int32BitsUnalignedImpl(ByteOrder byteOrder, int preBits, int val32) throws IOException { + final int preBytes = ( preBits + 7 ) >>> 3; + final int byteCount = preBytes + Buffers.SIZEOF_INT; + final ByteBuffer bb = ByteBuffer.allocate(byteCount); + if( null != byteOrder ) { + bb.order(byteOrder); + } + final boolean bigEndian = ByteOrder.BIG_ENDIAN == bb.order(); + System.err.println("XXX Test02Int32BitsUnaligned: byteOrder "+byteOrder+" (bigEndian "+bigEndian+"), preBits "+preBits+", value "+val32+", "+toHexBinaryString(val32, 32)); + System.err.println("XXX Test02Int32BitsUnaligned: "+toUnsignedBinaryString(val32)); + + // Test with written bitstream value + final Bitstream.ByteBufferStream bbs = new Bitstream.ByteBufferStream(bb); + final Bitstream<ByteBuffer> bs = new Bitstream<ByteBuffer>(bbs, true /* outputMode */); + bs.writeBits31(true /* msbFirst */, preBits, 0); + bs.writeInt32(true /* msbFirst */, bigEndian, val32); + bs.setStream(bs.getSubStream(), false /* outputMode */); // switch to input-mode, implies flush() + + final int rPre = bs.readBits31(true /* msbFirst */, preBits); + final int r32 = bs.readInt32(true /* msbFirst */, bigEndian); + System.err.println("ReadPre "+rPre+", "+toBinaryString(rPre, preBits)); + System.err.println("Read32 "+r32+", "+toHexBinaryString(r32, 32)+", "+toUnsignedBinaryString(r32)); + Assert.assertEquals(val32, r32); + } + + public static void main(String args[]) throws IOException { + String tstname = TestBitstream04.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} |