/* * %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); } 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); } } /** * Create a bitstr from a stream containing a bitstr encoded in Erlang external * format. * * @param buf * the stream containing the encoded bitstr. * * @exception 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; } /** * 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. */ @Override 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. */ @Override 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. */ @Override 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; } @Override protected int doHashCode() { final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(15); hash.combine(bin); hash.combine(pad_bits); return hash.valueOf(); } @Override public Object clone() { final OtpErlangBitstr that = (OtpErlangBitstr) super.clone(); that.bin = bin.clone(); that.pad_bits = pad_bits; return that; } }