/******************************************************************************* * Copyright © 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.javart; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.eclipse.edt.javart.resources.Platform; import org.eclipse.edt.javart.util.NumericUtil; /** * Utility class for persisting and reconstituting EGL runtime data to byte * arrays. It can be configured to automatically do data conversion. Conversion * is enabled by calling setConversion. When enabled, the load methods (except * loadByte and loadBytes) convert to the local format and the store methods * (except storeByte and storeBytes) convert from the local format. If conversion * is enabled and you are reading or writing directly to the buffer, then you * must do the conversion yourself. * <P> * Character conversion is done in loadString and storeString. If the encoding * is a Bidi encoding and the Unicode flag is true, the conversion is a two-stage * process. Otherwise, conversion uses either the encoding or the Unicode flag, * but not both. */ public class ByteStorage implements Cloneable, Serializable { private static final long serialVersionUID = Constants.SERIAL_VERSION_UID; /** * Value for the byteOrder property. */ public static final byte BYTEORDER_BIG_ENDIAN = 1; /** * Value for the byteOrder property. */ public static final byte BYTEORDER_LITTLE_ENDIAN = 2; /** * Value for the byteOrder property. */ public static final byte BYTEORDER_UNIX = 3; /** * Value for the reference = null. */ public static final byte REF_NULL = 0x0; /** * Value for the reference not = null. */ public static final byte REF_NOT_NULL = 0x01; /** * On EBCDIC systems, this value signifies the start of double-byte characters. * It must eventually be followed by SHIFT_IN. */ public final static byte SHIFT_OUT = 0x0E; /** * On EBCDIC systems, this value signifies the end of double-byte characters * until the next SHIFT_OUT. */ public final static byte SHIFT_IN = 0x0F; /** * The buffer containing the data. */ private byte[] buffer; /** * The position of the read/write pointer in the buffer. */ public int position; /** * The byte order used to store the data (BYTEORDER_xxx). */ private byte byteOrder; /** * The character encoding in effect. Null means use the local encoding. */ private String encoding; /** * True if the encoding indicates BIDI conversion. */ private boolean isBidi; /** * True for ASCII, false for EBCDIC. */ private boolean isAscii; /** * True if character data is converted to Unicode. */ private boolean isUnicode; /** * True if floating point numbers are in IEEE format. */ private boolean isIeeeFloat; /** * Provides guidance for certain conversions. When we're converting data to * bytes, it's true if they'll be passed to EGL Java code. When we're converting * data from bytes, it's true if they were produced by EGL Java code. The * default value is true. */ private boolean eglJavaFormat; /** * Constructor for storing data. * <p> * The internal buffer will be allocated with the desired initial capacity, * and can grow dynamically if output fills it up. * <P> * By default, data is not converted from the local format. * * @param capacity * The initial capacity */ public ByteStorage( int capacity ) { this( new byte[ capacity ] ); } /** * Constructor for loading and/or storing data. * <p> * The data buffer can grow if written to, so changes will not necessarily * appear in the array the caller passes to the constructor. * <P> * By default, data is not converted from the local format. * * @param buffer * The data buffer to read from */ public ByteStorage( byte[] buffer ) { this.buffer = buffer; this.position = 0; // Don't do conversion. this.byteOrder = BYTEORDER_BIG_ENDIAN; this.encoding = null; this.isAscii = Platform.IS_ASCII; this.isBidi = false; this.isUnicode = false; this.isIeeeFloat = true; this.eglJavaFormat = true; } /** * Return the data buffer that has been written. * * This returns the actual internal buffer, so if the caller changes the * contents, it will affect the state of this ByteStorage. * * However, if more data is added to this ByteStorage, the internal buffer * can be reallocated to contain the additional data, and in that case the * buffer previously returned by this method will no longer reflect the * current state of this object. * * @return The buffer */ public byte[] getBytes() { return this.buffer; } /** * Return a copy of the current contents of the data buffer. * * This returns a copy of the actual internal buffer, containing bytes from * the start to the current position. * * @return Copy of the buffer */ public byte[] getBytesCopy() { byte[] retbytes = new byte[ this.position ]; System.arraycopy( this.buffer, 0, retbytes, 0, this.position ); return retbytes; } /** * Return the current in/out position * * @return The position */ public int getPosition() { return this.position; } /** * Set the current in/out position. If it is beyond the end of the buffer, * the buffer is expanded (if allowed). * * @param position * The position */ public void setPosition( int position ) { if ( position > this.buffer.length ) { ensureCapacity( position ); } this.position = position; } /** * Resize the internal buffer to a specific length. This can truncate data, * and repositions the in/out pointer if necessary. * * @param size * The desired length */ private void reallocate( int size ) { byte[] newbuf = new byte[ size ]; int copylen = Math.min( this.buffer.length, size ); System.arraycopy( this.buffer, 0, newbuf, 0, copylen ); this.buffer = newbuf; if ( this.position > size ) { this.position = size; } } /** * If necessary, increase the capacity of the storage to at least a desired * size. * * @param need * Minimum size necessary */ public void ensureCapacity( int need ) { if ( this.buffer.length < need ) { reallocate( Math.max( need, this.buffer.length * 2 ) ); } } /** * Call this to alter what conversion is done. */ public void setConversion( byte byteOrder, String encoding, boolean isAscii, boolean isBidi, boolean isUnicode, boolean isIeeeFloat ) { this.byteOrder = byteOrder; this.encoding = encoding; this.isAscii = isAscii; this.isUnicode = isUnicode; this.isIeeeFloat = isIeeeFloat; // isBidi must be false when the encoding is null. if ( encoding == null ) { this.isBidi = false; } else { this.isBidi = isBidi; } } /** * Call this to turn off conversion. It is equivalent to: * <PRE> * setConversion( BYTEORDER_BIG_ENDIAN, null, Platform.IS_ASCII, false, false, true ); * setEglJavaFormat( true ); * </PRE> */ public void setNoConversion() { setConversion( BYTEORDER_BIG_ENDIAN, null, Platform.IS_ASCII, false, false, true ); setEglJavaFormat( true ); } /** * Changes the underlying buffer and sets the position to zero. Does not * change the conversion settings. * * @param newBuffer the new buffer. */ public void reset( byte[] newBuffer ) { buffer = newBuffer; position = 0; } /** * Add a number of blanks to the output stream. * * @param count how many blanks to add. * @return the new position. */ public int storeBlanks( int count ) { if ( isUnicode ) { // Store blanks from Constants.FIFTY_UNICODE_BLANK_BYTES. byte[] blanks = Constants.FIFTY_UNICODE_BLANK_BYTES; while ( count >= 50 ) { storeBytes( blanks, 0, 100 ); count -= 50; } if ( count > 0 ) { storeBytes( blanks, 0, count * 2 ); } } else if ( encoding == null || isAscii == Platform.IS_ASCII ) { // Store blanks from Constants.HUNDRED_BLANK_BYTES. byte[] blanks = Constants.HUNDRED_BLANK_BYTES; while ( count >= 100 ) { storeBytes( blanks, 0, 100 ); count -= 100; } if ( count > 0 ) { storeBytes( blanks, 0, count ); } } else { int newPosition = position + count; ensureCapacity( newPosition ); Arrays.fill( buffer, position, newPosition, isAscii ? (byte)0x20 : (byte)0x40 ); position = newPosition; } return position; } /** * Add a byte to the output stream. Does no conversion. * * @param value * The value * @return The new position */ public int storeByte( int value ) { int pos = this.position; try { this.buffer[ pos++ ] = (byte)value; this.position = pos; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 1 ); pos = this.position; this.buffer[ pos++ ] = (byte)value; this.position = pos; } return this.position; } /** * Add bytes to the output stream. Does no conversion. * * @param value * The value * @param start * The starting offset in the input buffer (value) * @param length * How many bytes to write * @return The new position */ public int storeBytes( byte[] value, int start, int length ) { try { System.arraycopy( value, start, this.buffer, this.position, length ); this.position += length; } catch ( IndexOutOfBoundsException e ) { ensureCapacity( this.position + length ); System.arraycopy( value, start, this.buffer, this.position, length ); this.position += length; } return this.position; } /** * Add bytes to the output stream. Does no conversion. * * @param value * The value * @return The new position */ public int storeBytes( byte[] value ) { return storeBytes( value, 0, value.length ); } /** * Add a short to the output stream. Does conversion according to the * byte order. * * @param value * The value * @return The new position */ public int storeShort( int value ) { int pos = this.position; try { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { this.buffer[ pos ] = (byte)(value >> 8); this.buffer[ pos + 1 ] = (byte)value; } else { this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; } this.position = pos + 2; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 2 ); if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { this.buffer[ pos ] = (byte)(value >> 8); this.buffer[ pos + 1 ] = (byte)value; } else { this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; } this.position = pos + 2; } return this.position; } /** * Add a short to the output stream. Does conversion according to the * byte order. The value must be in big-endian format. * * @param value * The 2-byte value * @param start * The first byte of the value * @return The new position */ public int storeShort( byte[] value, int start ) { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { return storeBytes( value, start, 2 ); } else { int shortValue = ((value[ start ] & 0xFF) << 8) | (value[ start + 1 ] & 0xFF); return storeShort( shortValue ); } } /** * Add an int to the output stream. Does conversion according to the * byte order. * * @param value * The value * @return The new position */ public int storeInt( int value ) { int pos = this.position; try { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { this.buffer[ pos ] = (byte)(value >> 24); this.buffer[ pos + 1 ] = (byte)(value >> 16); this.buffer[ pos + 2 ] = (byte)(value >> 8); this.buffer[ pos + 3 ] = (byte)value; } else { this.buffer[ pos + 3 ] = (byte)(value >> 24); this.buffer[ pos + 2 ] = (byte)(value >> 16); this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; } this.position = pos + 4; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 4 ); if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { this.buffer[ pos ] = (byte)(value >> 24); this.buffer[ pos + 1 ] = (byte)(value >> 16); this.buffer[ pos + 2 ] = (byte)(value >> 8); this.buffer[ pos + 3 ] = (byte)value; } else { this.buffer[ pos + 3 ] = (byte)(value >> 24); this.buffer[ pos + 2 ] = (byte)(value >> 16); this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; } this.position = pos + 4; } return this.position; } /** * Add an int to the output stream. Does conversion according to the * byte order. The value must be in big-endian format. * * @param value * The 4-byte value * @param start * The first byte of the value * @return The new position */ public int storeInt( byte[] value, int start ) { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { return storeBytes( value, start, 4 ); } else { int intValue = ((value[ start ] & 0xFF) << 24) | ((value[ start + 1 ] & 0xFF) << 16) | ((value[ start + 2 ] & 0xFF) << 8) | (value[ start + 3 ] & 0xFF); return storeInt( intValue ); } } /** * Add a long to the output stream. Does conversion according to the * byte order. * * @param value * The value * @return The new position */ public int storeLong( long value ) { int pos = this.position; try { switch ( byteOrder ) { case BYTEORDER_BIG_ENDIAN: this.buffer[ pos ] = (byte)(value >> 56); this.buffer[ pos + 1 ] = (byte)(value >> 48); this.buffer[ pos + 2 ] = (byte)(value >> 40); this.buffer[ pos + 3 ] = (byte)(value >> 32); this.buffer[ pos + 4 ] = (byte)(value >> 24); this.buffer[ pos + 5 ] = (byte)(value >> 16); this.buffer[ pos + 6 ] = (byte)(value >> 8); this.buffer[ pos + 7 ] = (byte)value; break; case BYTEORDER_LITTLE_ENDIAN: this.buffer[ pos + 7 ] = (byte)(value >> 56); this.buffer[ pos + 6 ] = (byte)(value >> 48); this.buffer[ pos + 5 ] = (byte)(value >> 40); this.buffer[ pos + 4 ] = (byte)(value >> 32); this.buffer[ pos + 3 ] = (byte)(value >> 24); this.buffer[ pos + 2 ] = (byte)(value >> 16); this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; break; case BYTEORDER_UNIX: // Same as BIG_ENDIAN but swap the first/last four bytes. this.buffer[ pos + 4 ] = (byte)(value >> 56); this.buffer[ pos + 5 ] = (byte)(value >> 48); this.buffer[ pos + 6 ] = (byte)(value >> 40); this.buffer[ pos + 7 ] = (byte)(value >> 32); this.buffer[ pos ] = (byte)(value >> 24); this.buffer[ pos + 1 ] = (byte)(value >> 16); this.buffer[ pos + 2 ] = (byte)(value >> 8); this.buffer[ pos + 3 ] = (byte)value; break; } this.position = pos + 8; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 8 ); switch ( byteOrder ) { case BYTEORDER_BIG_ENDIAN: this.buffer[ pos ] = (byte)(value >> 56); this.buffer[ pos + 1 ] = (byte)(value >> 48); this.buffer[ pos + 2 ] = (byte)(value >> 40); this.buffer[ pos + 3 ] = (byte)(value >> 32); this.buffer[ pos + 4 ] = (byte)(value >> 24); this.buffer[ pos + 5 ] = (byte)(value >> 16); this.buffer[ pos + 6 ] = (byte)(value >> 8); this.buffer[ pos + 7 ] = (byte)value; break; case BYTEORDER_LITTLE_ENDIAN: this.buffer[ pos + 7 ] = (byte)(value >> 56); this.buffer[ pos + 6 ] = (byte)(value >> 48); this.buffer[ pos + 5 ] = (byte)(value >> 40); this.buffer[ pos + 4 ] = (byte)(value >> 32); this.buffer[ pos + 3 ] = (byte)(value >> 24); this.buffer[ pos + 2 ] = (byte)(value >> 16); this.buffer[ pos + 1 ] = (byte)(value >> 8); this.buffer[ pos ] = (byte)value; break; case BYTEORDER_UNIX: // Same as BIG_ENDIAN but swap the first/last four bytes. this.buffer[ pos + 4 ] = (byte)(value >> 56); this.buffer[ pos + 5 ] = (byte)(value >> 48); this.buffer[ pos + 6 ] = (byte)(value >> 40); this.buffer[ pos + 7 ] = (byte)(value >> 32); this.buffer[ pos ] = (byte)(value >> 24); this.buffer[ pos + 1 ] = (byte)(value >> 16); this.buffer[ pos + 2 ] = (byte)(value >> 8); this.buffer[ pos + 3 ] = (byte)value; break; } this.position = pos + 8; } return this.position; } /** * Add a long to the output stream. Does conversion according to the * byte order. The value must be in big-endian format. * * @param value * The 8-byte value * @param start * The first byte of the value * @return The new position */ public int storeLong( byte[] value, int start ) { if ( byteOrder == BYTEORDER_BIG_ENDIAN ) { return storeBytes( value, start, 8 ); } else { long longValue = ((value[ start ] & 0xFFL) << 56) | ((value[ start + 1 ] & 0xFFL) << 48) | ((value[ start + 2 ] & 0xFFL) << 40) | ((value[ start + 3 ] & 0xFFL) << 32) | ((value[ start + 4 ] & 0xFFL) << 24) | ((value[ start + 5 ] & 0xFFL) << 16) | ((value[ start + 6 ] & 0xFFL) << 8) | (value[ start + 7 ] & 0xFFL); return storeLong( longValue ); } } /** * Add the bytes of a float to the input stream. Does conversion according * to the IEEE flag. * * @param value * The value * @return The new position */ public int storeFloat( float value ) { int pos = this.position; int bits; if ( isIeeeFloat ) { bits = Float.floatToRawIntBits( value ); } else { bits = NumericUtil.floatToS390IntBits( value ); } try { this.buffer[ pos ] = (byte)(bits >> 24); this.buffer[ pos + 1 ] = (byte)(bits >> 16); this.buffer[ pos + 2 ] = (byte)(bits >> 8); this.buffer[ pos + 3 ] = (byte)bits; this.position = pos + 4; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 4 ); this.buffer[ pos ] = (byte)(bits >> 24); this.buffer[ pos + 1 ] = (byte)(bits >> 16); this.buffer[ pos + 2 ] = (byte)(bits >> 8); this.buffer[ pos + 3 ] = (byte)bits; this.position = pos + 4; } return this.position; } /** * Add the bytes of a double to the input stream. Does conversion according * to the IEEE flag. * * @param value * The value * @return The new position */ public int storeDouble( double value ) { int pos = this.position; long bits; if ( isIeeeFloat ) { bits = Double.doubleToRawLongBits( value ); } else { bits = NumericUtil.doubleToS390LongBits( value ); } try { this.buffer[ pos ] = (byte)(bits >> 56); this.buffer[ pos + 1 ] = (byte)(bits >> 48); this.buffer[ pos + 2 ] = (byte)(bits >> 40); this.buffer[ pos + 3 ] = (byte)(bits >> 32); this.buffer[ pos + 4 ] = (byte)(bits >> 24); this.buffer[ pos + 5 ] = (byte)(bits >> 16); this.buffer[ pos + 6 ] = (byte)(bits >> 8); this.buffer[ pos + 7 ] = (byte)bits; this.position = pos + 8; } catch ( ArrayIndexOutOfBoundsException e ) { ensureCapacity( this.position + 8 ); this.buffer[ pos ] = (byte)(bits >> 56); this.buffer[ pos + 1 ] = (byte)(bits >> 48); this.buffer[ pos + 2 ] = (byte)(bits >> 40); this.buffer[ pos + 3 ] = (byte)(bits >> 32); this.buffer[ pos + 4 ] = (byte)(bits >> 24); this.buffer[ pos + 5 ] = (byte)(bits >> 16); this.buffer[ pos + 6 ] = (byte)(bits >> 8); this.buffer[ pos + 7 ] = (byte)bits; this.position = pos + 8; } return this.position; } /** * Add the bytes of a String to the input stream. Does conversion according * to the encoding and isUnicode flag. * * @param value * The value * @return The new position */ public int storeString( String value ) { return storeString( value, false ); } /** * Add a dbchar's bytes to the input stream. Does conversion according to * the encoding and isUnicode flag. * * @param value * The value * @return The new position */ public int storeDbchar( String value ) { return storeString( value, true ); } /** * Add the bytes of a String to the input stream. Does conversion according * to the encoding and isUnicode flag. * * @param value * The value * @param stripSOSI * True if we should remove shift-in and shift-out bytes before * storing the value in the buffer. * @return The new position */ private int storeString( String value, boolean stripSOSI ) { byte[] bytes; if ( encoding == null && !isUnicode ) { // Use the local format. bytes = value.getBytes(); if ( stripSOSI ) { bytes = stripSOSI( bytes ); } } else if ( encoding != null ) { // Use the encoding. try { bytes = value.getBytes( encoding ); if ( stripSOSI ) { bytes = stripSOSI( bytes ); } } catch ( UnsupportedEncodingException uex ) { bytes = value.getBytes(); if ( stripSOSI ) { bytes = stripSOSI( bytes ); } } } else { // Convert to Unicode. bytes = new byte[ value.length() * 2 ]; for ( int i = 0, j = 0; i < bytes.length; i += 2, j++ ) { char c = value.charAt( j ); bytes[ i ] = (byte)(c >> 8 ); bytes[ i + 1 ] = (byte)c; } } return storeBytes( bytes, 0, bytes.length ); } /** * If the input starts with shift-out and ends with shift-in, returns a new * array that doesn't include them. Otherwise returns the input. * * @param bytes the bytes to check. * @return bytes without SOSI. */ private byte[] stripSOSI( byte[] bytes ) { if ( bytes[ 0 ] != SHIFT_OUT || bytes[ bytes.length - 1 ] != SHIFT_IN ) { return bytes; } else { byte[] temp = new byte[ bytes.length - 2 ]; System.arraycopy( bytes, 1, temp, 0, temp.length ); return temp; } } /** * Load a byte from the input stream. Does no conversion. * * @return The value */ public byte loadByte() { int pos = this.position; byte value = this.buffer[ pos++ ]; this.position = pos; return value; } /** * Load a short from the input stream. Does conversion according to the * byte order. * * @return The value */ public short loadShort() { int pos = this.position; int value; if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { value = ((this.buffer[ pos ] & 0xFF) << 8) // | (this.buffer[ pos + 1 ] & 0xFF); } else { value = ((this.buffer[ pos + 1 ] & 0xFF) << 8) // | (this.buffer[ pos ] & 0xFF); } this.position = pos + 2; return (short)value; } /** * Load a short from the input stream. Does conversion according to the * byte order. The result will be in big-endian format. * * @param value * Where to put the 2-byte value * @param start * The index for the first byte of the value */ public void loadShort( byte[] value, int start ) { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { loadBytes( value, start, 2 ); } else { short shortValue = loadShort(); value[ start ] = (byte)(shortValue >> 8); value[ start + 1 ] = (byte)shortValue; } } /** * Load an int from the input stream. Does conversion according to the * byte order. * * @return The value */ public int loadInt() { int pos = this.position; int value; if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { value = ((this.buffer[ pos ] & 0xFF) << 24) // | ((this.buffer[ pos + 1 ] & 0xFF) << 16) // | ((this.buffer[ pos + 2 ] & 0xFF) << 8) // | (this.buffer[ pos + 3 ] & 0xFF); } else { value = ((this.buffer[ pos + 3 ] & 0xFF) << 24) // | ((this.buffer[ pos + 2 ] & 0xFF) << 16) // | ((this.buffer[ pos + 1 ] & 0xFF) << 8) // | (this.buffer[ pos ] & 0xFF); } this.position = pos + 4; return value; } /** * Load an int from the input stream. Does conversion according to the * byte order. The result will be in big-endian format. * * @param value * Where to put the 4-byte value * @param start * The index for the first byte of the value */ public void loadInt( byte[] value, int start ) { if ( byteOrder != BYTEORDER_LITTLE_ENDIAN ) { loadBytes( value, start, 4 ); } else { int intValue = loadInt(); value[ start ] = (byte)(intValue >> 24); value[ start + 1 ] = (byte)(intValue >> 16); value[ start + 2 ] = (byte)(intValue >> 8); value[ start + 3 ] = (byte)intValue; } } /** * Load a long from the input stream. Does conversion according to the * byte order. * * @return The value */ public long loadLong() { int pos = this.position; long value; switch ( byteOrder ) { case BYTEORDER_BIG_ENDIAN: value = ((this.buffer[ pos ] & 0xFFL) << 56) // | ((this.buffer[ pos + 1 ] & 0xFFL) << 48) // | ((this.buffer[ pos + 2 ] & 0xFFL) << 40) // | ((this.buffer[ pos + 3 ] & 0xFFL) << 32) // | ((this.buffer[ pos + 4 ] & 0xFFL) << 24) // | ((this.buffer[ pos + 5 ] & 0xFFL) << 16) // | ((this.buffer[ pos + 6 ] & 0xFFL) << 8) // | (this.buffer[ pos + 7 ] & 0xFFL); break; case BYTEORDER_LITTLE_ENDIAN: value = ((this.buffer[ pos + 7 ] & 0xFFL) << 56) // | ((this.buffer[ pos + 6 ] & 0xFFL) << 48) // | ((this.buffer[ pos + 5 ] & 0xFFL) << 40) // | ((this.buffer[ pos + 4 ] & 0xFFL) << 32) // | ((this.buffer[ pos + 3 ] & 0xFFL) << 24) // | ((this.buffer[ pos + 2 ] & 0xFFL) << 16) // | ((this.buffer[ pos + 1 ] & 0xFFL) << 8) // | (this.buffer[ pos ] & 0xFFL); break; default: // BYTEORDER_UNIX // Same as BIG_ENDIAN but swap the first/last four bytes. value = ((this.buffer[ pos ] & 0xFFL) << 24) // | ((this.buffer[ pos + 1 ] & 0xFFL) << 16) // | ((this.buffer[ pos + 2 ] & 0xFFL) << 8) // | (this.buffer[ pos + 3 ] & 0xFFL) // | ((this.buffer[ pos + 4 ] & 0xFFL) << 56) // | ((this.buffer[ pos + 5 ] & 0xFFL) << 48) // | ((this.buffer[ pos + 6 ] & 0xFFL) << 40) // | ((this.buffer[ pos + 7 ] & 0xFFL) << 32); break; } this.position = pos + 8; return value; } /** * Load a long from the input stream. Does conversion according to the * byte order. The result will be in big-endian format. * * @param value * Where to put the 8-byte value * @param start * The index for the first byte of the value */ public void loadLong( byte[] value, int start ) { if ( byteOrder == BYTEORDER_BIG_ENDIAN ) { loadBytes( value, start, 8 ); } else { long longValue = loadLong(); value[ start ] = (byte)(longValue >> 56); value[ start + 1 ] = (byte)(longValue >> 48); value[ start + 2 ] = (byte)(longValue >> 40); value[ start + 3 ] = (byte)(longValue >> 32); value[ start + 4 ] = (byte)(longValue >> 24); value[ start + 5 ] = (byte)(longValue >> 16); value[ start + 6 ] = (byte)(longValue >> 8); value[ start + 7 ] = (byte)longValue; } } /** * Load bytes from the input stream. Does no conversion. * * @param value * The buffer to receive the data * @param valstart * The starting offset in the output buffer * @param length * How many bytes to read * @return The new position */ public int loadBytes( byte[] value, int valstart, int length ) { System.arraycopy( this.buffer, this.position, value, valstart, length ); this.position += length; return this.position; } /** * Load bytes from the output stream. Does no conversion. * * @param value * The value * @return The new position */ public int loadBytes( byte[] value ) { return loadBytes( value, 0, value.length ); } /** * Load bytes from the output stream as a String. Does conversion according * to the encoding and isUnicode flag. * * @param byteLength * How many bytes to turn into a String, if the value is not stored * in Unicode. When the value is in Unicode, the number of bytes * read equals byteLength * unicodeExpansion. * @param unicodeExpansion * The ratio of the size of a unicode character to the size of a * non-unicode character. For char items the value should be 2 * because a unicode character is two bytes and a non-unicode * character in a char item is one byte. * @return The String. */ public String loadString( int byteLength, int unicodeExpansion ) { return loadString( byteLength, unicodeExpansion, false ); } /** * Load a dbchar's bytes from the output stream as a String. Does conversion * according to the encoding and isUnicode flag. * * @param byteLength * How many bytes to turn into a String, if the value is not stored * in Unicode. When the value is in Unicode, the number of bytes * read equals byteLength * unicodeExpansion. * @return The String. */ public String loadDbchar( int byteLength ) { return loadString( byteLength, 1, true ); } /** * Load bytes from the output stream as a String. Does conversion according * to the encoding and isUnicode flag. * * @param byteLength * How many bytes to turn into a String, if the value is not stored * in Unicode. When the value is in Unicode, the number of bytes * read equals byteLength * unicodeExpansion. * @param unicodeExpansion * The ratio of the size of a unicode character to the size of a * non-unicode character. For char items the value should be 2 * because a unicode character is two bytes and a non-unicode * character in a char item is one byte. * @param addSOSI * True if the bytes don't include shift-in and shift-out chars * necessary on EBCDIC systems. * @return The String. */ private String loadString( int byteLength, int unicodeExpansion, boolean addSOSI ) { // If the data is in Unicode, adjust byteLength. if ( isUnicode && (encoding == null || isBidi) ) { byteLength *= unicodeExpansion; } // Read the data. byte[] bytes = new byte[ byteLength ]; loadBytes( bytes, 0, byteLength ); // Convert the data. if ( encoding != null || !isUnicode ) { return bytesToString( bytes, encoding, addSOSI ); } else { // Convert from Unicode. char[] chars = new char[ byteLength / 2 ]; for ( int i = 0, j = 0; i < chars.length; i++, j += 2 ) { chars[ i ] = (char)(bytes[ j ] << 8); chars[ i ] |= (bytes[ j + 1 ] & 0xFF); } return new String( chars ); } } /** * Converts a byte array to a string, using the specified codepage. * * @param bytes the bytes. * @param encoding the encoding, null means use the local codepage. * @param addSOSI true if SO/SI bytes need to be added before & after the bytes, * on EBCDIC systems. * @return the bytes converted to a String. */ private String bytesToString( byte[] bytes, String encoding, boolean addSOSI ) { try { if ( !isAscii && addSOSI && bytes[ 0 ] != SHIFT_OUT && bytes[ bytes.length - 1 ] != SHIFT_IN ) { // Add SO/SI bytes. byte[] temp = new byte[ bytes.length + 2 ]; System.arraycopy( bytes, 0, temp, 1, bytes.length ); temp[ 0 ] = SHIFT_OUT; temp[ temp.length - 1 ] = SHIFT_IN; bytes = temp; } String str = encoding != null ? new String( bytes, encoding ) : new String( bytes ); // Remove the SO/SI bytes from the result since they're not part of the value. if ( !isAscii && addSOSI && str.charAt( 0 ) == SHIFT_OUT && str.charAt( str.length() - 1 ) == SHIFT_IN ) { str = str.substring( 1, str.length() + 1 ); } return str; } catch ( UnsupportedEncodingException uex ) { // Won't happen. return new String( bytes ); } } /** * Load a float from the input stream. Does conversion according to the * IEEE flag. * * @return The value */ public float loadFloat() { int pos = this.position; int bits = ((buffer[ pos ] & 0xFF) << 24) // | ((buffer[ pos + 1 ] & 0xFF) << 16) // | ((buffer[ pos + 2 ] & 0xFF) << 8) // | (buffer[ pos + 3 ] & 0xFF); this.position = pos + 4; float value; if ( isIeeeFloat ) { value = Float.intBitsToFloat( bits ); } else { value = NumericUtil.intS390BitsToFloat( bits ); } return value; } /** * Load a double from the input stream. Does conversion according to the * IEEE flag. * * @return The value */ public double loadDouble() { int pos = this.position; long bits = ((buffer[ pos ] & 0xFFL) << 56) // | ((buffer[ pos + 1 ] & 0xFFL) << 48) // | ((buffer[ pos + 2 ] & 0xFFL) << 40) // | ((buffer[ pos + 3 ] & 0xFFL) << 32) // | ((buffer[ pos + 4 ] & 0xFFL) << 24) // | ((buffer[ pos + 5 ] & 0xFFL) << 16) // | ((buffer[ pos + 6 ] & 0xFFL) << 8) // | (buffer[ pos + 7 ] & 0xFFL); this.position = pos + 8; double value; if ( isIeeeFloat ) { value = Double.longBitsToDouble( bits ); } else { value = NumericUtil.longS390BitsToDouble( bits ); } return value; } /** * Get the number of bytes remaining in the buffer from the current position * * @return The number of bytes */ public int getNumBytesRemaining() { return this.buffer.length - this.position; } /** * @return the byte order used to store the data (BYTEORDER_xxx). */ public byte getByteOrder() { return byteOrder; } /** * @return the character encoding in effect. Null means we're using the local * encoding. */ public String getEncoding() { return encoding; } /** * @return true for ASCII, false for EBCDIC. */ public boolean isAscii() { return isAscii; } /** * @return true if the encoding indicates BIDI conversion. Will return false * if the encoding is null. */ public boolean isBidi() { return isBidi; } /** * @return true if character data is converted to Unicode. */ public boolean isUnicode() { return isUnicode; } /** * @return true if floating point numbers are in IEEE format. */ public boolean isIeeeFloat() { return isIeeeFloat; } /** * Provides guidance for certain conversions. When we're converting data to * bytes, returns true if they'll be passed to EGL Java code. When we're converting * data from bytes, returns true if they were produced by EGL Java code. */ public boolean isEglJavaFormat() { return eglJavaFormat; } /** * Sets the eglJavaFormat flag. */ public void setEglJavaFormat( boolean eglJava ) { eglJavaFormat = eglJava; } /** * Returns a String showing the class name, current position, and length. */ public String toString() { return "ByteStorage[pos=" + position + " of " + buffer.length + ']'; } /** * Returns a clone of this object. */ public Object clone() throws CloneNotSupportedException { ByteStorage theClone = (ByteStorage)super.clone(); buffer = (byte[])buffer.clone(); return theClone; } }