aboutsummaryrefslogtreecommitdiffstats
path: root/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
blob: 68e438cd27d269c13ffc127375c2e38563ada994 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
 * %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 {
    protected int hashCodeValue = 0;

    // don't change this!
    static final long serialVersionUID = -8435938572339430044L;

    /**
     * @return the printable representation of the object. This is usually
     *         similar to the representation used by Erlang for the same type of
     *         object.
     */
    @Override
    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);

    /**
     * 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.
     *
     * @exception 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();
    }

    /**
     * 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.
     */
    @Override
    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;
    }

    @Override
    public int hashCode() {
        if (hashCodeValue == 0) {
            hashCodeValue = doHashCode();
        }
        return hashCodeValue;
    }

    protected int doHashCode() {
        return super.hashCode();
    }

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (final CloneNotSupportedException e) {
            /* cannot happen */
            throw new InternalError(e.toString());
        }
    }

    protected final static class Hash {
        int abc[] = { 0, 0, 0 };

        /*
         * Hash function suggested by Bob Jenkins. The same as in the Erlang VM
         * (beam); utils.c.
         */

        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
        };

        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];
        }
    }
}