/*
|
* %CopyrightBegin%
|
*
|
* Copyright Ericsson AB 2007-2009. All Rights Reserved.
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*
|
* %CopyrightEnd%
|
*/
|
package com.ericsson.otp.erlang;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.IOException;
|
|
/**
|
* Provides a Java representation of Erlang bitstrs. An Erlang bitstr is an
|
* Erlang binary with a length not an integral number of bytes (8-bit). Anything
|
* can be represented as a sequence of bytes can be made into an Erlang bitstr.
|
*/
|
public class OtpErlangBitstr extends OtpErlangObject {
|
// don't change this!
|
private static final long serialVersionUID = -3781009633593609217L;
|
protected byte[] bin;
|
protected int pad_bits;
|
|
/**
|
* Create a bitstr from a byte array
|
*
|
* @param bin the array of bytes from which to create the bitstr.
|
*/
|
public OtpErlangBitstr(final byte[] bin) {
|
this.bin = new byte[bin.length];
|
System.arraycopy(bin, 0, this.bin, 0, bin.length);
|
pad_bits = 0;
|
}
|
|
/**
|
* Create a bitstr with pad bits from a byte array.
|
*
|
* @param bin the array of bytes from which to create the bitstr.
|
* @param pad_bits the number of unused bits in the low end of the last byte.
|
*/
|
public OtpErlangBitstr(final byte[] bin, final int pad_bits) {
|
this.bin = new byte[bin.length];
|
System.arraycopy(bin, 0, this.bin, 0, bin.length);
|
this.pad_bits = pad_bits;
|
check_bitstr(this.bin, this.pad_bits);
|
}
|
|
/**
|
* Create a bitstr from a stream containing a bitstr encoded in Erlang
|
* external format.
|
*
|
* @param buf the stream containing the encoded bitstr.
|
* @throws OtpErlangDecodeException if the buffer does not contain a valid external
|
* representation of an Erlang bitstr.
|
*/
|
public OtpErlangBitstr(final OtpInputStream buf)
|
throws OtpErlangDecodeException {
|
final int pbs[] = {0}; // This is ugly just to get a value-result
|
// parameter
|
bin = buf.read_bitstr(pbs);
|
pad_bits = pbs[0];
|
check_bitstr(bin, pad_bits);
|
}
|
|
/**
|
* Create a bitstr from an arbitrary Java Object. The object must implement
|
* java.io.Serializable or java.io.Externalizable.
|
*
|
* @param o the object to serialize and create this bitstr from.
|
*/
|
public OtpErlangBitstr(final Object o) {
|
try {
|
bin = toByteArray(o);
|
pad_bits = 0;
|
} catch (final IOException e) {
|
throw new java.lang.IllegalArgumentException(
|
"Object must implement Serializable");
|
}
|
}
|
|
private static byte[] toByteArray(final Object o)
|
throws java.io.IOException {
|
if (o == null) {
|
return null;
|
}
|
/* need to synchronize use of the shared baos */
|
final java.io.ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
final java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(
|
baos);
|
oos.writeObject(o);
|
oos.flush();
|
return baos.toByteArray();
|
}
|
|
private static Object fromByteArray(final byte[] buf) {
|
if (buf == null) {
|
return null;
|
}
|
try {
|
final java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(
|
buf);
|
final java.io.ObjectInputStream ois = new java.io.ObjectInputStream(
|
bais);
|
return ois.readObject();
|
} catch (final java.lang.ClassNotFoundException e) {
|
} catch (final java.io.IOException e) {
|
}
|
return null;
|
}
|
|
private void check_bitstr(final byte[] abin, final int a_pad_bits) {
|
if (a_pad_bits < 0 || 7 < a_pad_bits) {
|
throw new java.lang.IllegalArgumentException(
|
"Padding must be in range 0..7");
|
}
|
if (a_pad_bits != 0 && abin.length == 0) {
|
throw new java.lang.IllegalArgumentException(
|
"Padding on zero length bitstr");
|
}
|
if (abin.length != 0) {
|
// Make sure padding is zero
|
abin[abin.length - 1] &= ~((1 << a_pad_bits) - 1);
|
}
|
}
|
|
/**
|
* Get the byte array from a bitstr, padded with zero bits in the little end
|
* of the last byte.
|
*
|
* @return the byte array containing the bytes for this bitstr.
|
*/
|
public byte[] binaryValue() {
|
return bin;
|
}
|
|
/**
|
* Get the size in whole bytes of the bitstr, rest bits in the last byte not
|
* counted.
|
*
|
* @return the number of bytes contained in the bintstr.
|
*/
|
public int size() {
|
if (pad_bits == 0) {
|
return bin.length;
|
}
|
if (bin.length == 0) {
|
throw new java.lang.IllegalStateException("Impossible length");
|
}
|
return bin.length - 1;
|
}
|
|
/**
|
* Get the number of pad bits in the last byte of the bitstr. The pad bits
|
* are zero and in the little end.
|
*
|
* @return the number of pad bits in the bitstr.
|
*/
|
public int pad_bits() {
|
return pad_bits;
|
}
|
|
/**
|
* Get the java Object from the bitstr. If the bitstr contains a serialized
|
* Java object, then this method will recreate the object.
|
*
|
* @return the java Object represented by this bitstr, or null if the bitstr
|
* does not represent a Java Object.
|
*/
|
public Object getObject() {
|
if (pad_bits != 0) {
|
return null;
|
}
|
return fromByteArray(bin);
|
}
|
|
/**
|
* Get the string representation of this bitstr object. A bitstr is printed
|
* as #Bin<N>, where N is the number of bytes contained in the object
|
* or #bin<N-M> if there are M pad bits.
|
*
|
* @return the Erlang string representation of this bitstr.
|
*/
|
public String toString() {
|
if (pad_bits == 0) {
|
return "#Bin<" + bin.length + ">";
|
}
|
if (bin.length == 0) {
|
throw new java.lang.IllegalStateException("Impossible length");
|
}
|
return "#Bin<" + bin.length + "-" + pad_bits + ">";
|
}
|
|
/**
|
* Convert this bitstr to the equivalent Erlang external representation.
|
*
|
* @param buf an output stream to which the encoded bitstr should be
|
* written.
|
*/
|
public void encode(final OtpOutputStream buf) {
|
buf.write_bitstr(bin, pad_bits);
|
}
|
|
/**
|
* Determine if two bitstrs are equal. Bitstrs are equal if they have the
|
* same byte length and tail length, and the array of bytes is identical.
|
*
|
* @param o the bitstr to compare to.
|
* @return true if the bitstrs contain the same bits, false otherwise.
|
*/
|
public boolean equals(final Object o) {
|
if (!(o instanceof OtpErlangBitstr)) {
|
return false;
|
}
|
final OtpErlangBitstr that = (OtpErlangBitstr) o;
|
if (pad_bits != that.pad_bits) {
|
return false;
|
}
|
final int len = bin.length;
|
if (len != that.bin.length) {
|
return false;
|
}
|
for (int i = 0; i < len; i++) {
|
if (bin[i] != that.bin[i]) {
|
return false; // early exit
|
}
|
}
|
return true;
|
}
|
|
protected int doHashCode() {
|
final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(15);
|
hash.combine(bin);
|
hash.combine(pad_bits);
|
return hash.valueOf();
|
}
|
|
public Object clone() {
|
final OtpErlangBitstr that = (OtpErlangBitstr) super.clone();
|
that.bin = bin.clone();
|
that.pad_bits = pad_bits;
|
return that;
|
}
|
}
|