/*
|
* %CopyrightBegin%
|
*
|
* Copyright Ericsson AB 2000-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.Serializable;
|
|
/**
|
* Base class of the Erlang data type classes. This class is used to represent
|
* an arbitrary Erlang term.
|
*/
|
public abstract class OtpErlangObject implements Serializable, Cloneable {
|
// don't change this!
|
static final long serialVersionUID = -8435938572339430044L;
|
protected int hashCodeValue = 0;
|
|
/**
|
* Read binary data in the Erlang external format, and produce a
|
* corresponding Erlang data type object. This method is normally used when
|
* Erlang terms are received in messages, however it can also be used for
|
* reading terms from disk.
|
*
|
* @param buf an input stream containing one or more encoded Erlang terms.
|
* @return an object representing one of the Erlang data types.
|
* @throws OtpErlangDecodeException if the stream does not contain a valid representation of
|
* an Erlang term.
|
*/
|
public static OtpErlangObject decode(final OtpInputStream buf)
|
throws OtpErlangDecodeException {
|
return buf.read_any();
|
}
|
|
/**
|
* @return the printable representation of the object. This is usually
|
* similar to the representation used by Erlang for the same type of
|
* object.
|
*/
|
public abstract String toString();
|
|
/**
|
* Convert the object according to the rules of the Erlang external format.
|
* This is mainly used for sending Erlang terms in messages, however it can
|
* also be used for storing terms to disk.
|
*
|
* @param buf an output stream to which the encoded term should be written.
|
*/
|
public abstract void encode(OtpOutputStream buf);
|
|
/**
|
* Determine if two Erlang objects are equal. In general, Erlang objects are
|
* equal if the components they consist of are equal.
|
*
|
* @param o the object to compare to.
|
* @return true if the objects are identical.
|
*/
|
public abstract boolean equals(Object o);
|
|
/**
|
* Perform match operation against given term.
|
*
|
* @param term the object to match
|
* @param binds variable bindings
|
* @return true if match succeeded
|
*/
|
public <T> boolean match(final OtpErlangObject term, final T binds) {
|
return equals(term);
|
}
|
|
/**
|
* Make new Erlang term replacing variables with the respective values from
|
* bindings argument(s).
|
*
|
* @param binds variable bindings
|
* @return new term
|
* @throws OtpErlangException
|
*/
|
public <T> OtpErlangObject bind(final T binds) throws OtpErlangException {
|
return this;
|
}
|
|
public int hashCode() {
|
if (hashCodeValue == 0) {
|
hashCodeValue = doHashCode();
|
}
|
return hashCodeValue;
|
}
|
|
protected int doHashCode() {
|
return super.hashCode();
|
}
|
|
public Object clone() {
|
try {
|
return super.clone();
|
} catch (final CloneNotSupportedException e) {
|
/* cannot happen */
|
throw new InternalError(e.toString());
|
}
|
}
|
|
protected final static class Hash {
|
private final static int HASH_CONST[] = {0, // not used
|
0x9e3779b9, // the golden ratio; an arbitrary value
|
0x3c6ef372, // (hashHConst[1] * 2) % (1<<32)
|
0xdaa66d2b, // 1 3
|
0x78dde6e4, // 1 4
|
0x1715609d, // 1 5
|
0xb54cda56, // 1 6
|
0x5384540f, // 1 7
|
0xf1bbcdc8, // 1 8
|
0x8ff34781, // 1 9
|
0x2e2ac13a, // 1 10
|
0xcc623af3, // 1 11
|
0x6a99b4ac, // 1 12
|
0x08d12e65, // 1 13
|
0xa708a81e, // 1 14
|
0x454021d7, // 1 15
|
};
|
/*
|
* Hash function suggested by Bob Jenkins. The same as in the Erlang VM
|
* (beam); utils.c.
|
*/
|
int abc[] = {0, 0, 0};
|
|
protected Hash(final int i) {
|
abc[0] = abc[1] = HASH_CONST[i];
|
abc[2] = 0;
|
}
|
|
// protected Hash() {
|
// Hash(1);
|
// }
|
private void mix() {
|
abc[0] -= abc[1];
|
abc[0] -= abc[2];
|
abc[0] ^= abc[2] >>> 13;
|
abc[1] -= abc[2];
|
abc[1] -= abc[0];
|
abc[1] ^= abc[0] << 8;
|
abc[2] -= abc[0];
|
abc[2] -= abc[1];
|
abc[2] ^= abc[1] >>> 13;
|
abc[0] -= abc[1];
|
abc[0] -= abc[2];
|
abc[0] ^= abc[2] >>> 12;
|
abc[1] -= abc[2];
|
abc[1] -= abc[0];
|
abc[1] ^= abc[0] << 16;
|
abc[2] -= abc[0];
|
abc[2] -= abc[1];
|
abc[2] ^= abc[1] >>> 5;
|
abc[0] -= abc[1];
|
abc[0] -= abc[2];
|
abc[0] ^= abc[2] >>> 3;
|
abc[1] -= abc[2];
|
abc[1] -= abc[0];
|
abc[1] ^= abc[0] << 10;
|
abc[2] -= abc[0];
|
abc[2] -= abc[1];
|
abc[2] ^= abc[1] >>> 15;
|
}
|
|
protected void combine(final int a) {
|
abc[0] += a;
|
mix();
|
}
|
|
protected void combine(final long a) {
|
combine((int) (a >>> 32), (int) a);
|
}
|
|
protected void combine(final int a, final int b) {
|
abc[0] += a;
|
abc[1] += b;
|
mix();
|
}
|
|
protected void combine(final byte b[]) {
|
int j, k;
|
for (j = 0, k = 0; j + 4 < b.length; j += 4, k += 1, k %= 3) {
|
abc[k] += (b[j + 0] & 0xFF) + (b[j + 1] << 8 & 0xFF00)
|
+ (b[j + 2] << 16 & 0xFF0000) + (b[j + 3] << 24);
|
mix();
|
}
|
for (int n = 0, m = 0xFF; j < b.length; j++, n += 8, m <<= 8) {
|
abc[k] += b[j] << n & m;
|
}
|
mix();
|
}
|
|
protected int valueOf() {
|
return abc[2];
|
}
|
}
|
}
|