JavaValueArray.java revision 2224:2a8815d86b93
1/*
2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27/*
28 * The Original Code is HAT. The Initial Developer of the
29 * Original Code is Bill Foote, with contributions from others
30 * at JavaSoft/Sun.
31 */
32
33package jdk.test.lib.hprof.model;
34
35import jdk.test.lib.hprof.parser.ReadBuffer;
36import java.io.IOException;
37
38/**
39 * An array of values, that is, an array of ints, boolean, floats or the like.
40 *
41 * @author      Bill Foote
42 */
43public class JavaValueArray extends JavaLazyReadObject
44                /*imports*/ implements ArrayTypeCodes {
45
46    private static String arrayTypeName(byte sig) {
47        switch (sig) {
48            case 'B':
49                return "byte[]";
50            case 'Z':
51                return "boolean[]";
52            case 'C':
53                return "char[]";
54            case 'S':
55                return "short[]";
56            case 'I':
57                return "int[]";
58            case 'F':
59                return "float[]";
60            case 'J':
61                return "long[]";
62            case 'D':
63                return "double[]";
64            default:
65                throw new RuntimeException("invalid array element sig: " + sig);
66        }
67    }
68
69    private static int elementSize(byte type) {
70        switch (type) {
71            case T_BYTE:
72            case T_BOOLEAN:
73                return 1;
74            case T_CHAR:
75            case T_SHORT:
76                return 2;
77            case T_INT:
78            case T_FLOAT:
79                return 4;
80            case T_LONG:
81            case T_DOUBLE:
82                return 8;
83            default:
84                throw new RuntimeException("invalid array element type: " + type);
85        }
86    }
87
88    /*
89     * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks
90     * as below:
91     *
92     *    object ID
93     *    stack trace serial number (int)
94     *    length of the instance data (int)
95     *    element type (byte)
96     *    array data
97     */
98    protected final int readValueLength() throws IOException {
99        JavaClass cl = getClazz();
100        ReadBuffer buf = cl.getReadBuffer();
101        int idSize = cl.getIdentifierSize();
102        long offset = getOffset() + idSize + 4;
103        // length of the array
104        int len = buf.getInt(offset);
105        // typecode of array element type
106        byte type = buf.getByte(offset + 4);
107        return len * elementSize(type);
108    }
109
110    protected final byte[] readValue() throws IOException {
111        JavaClass cl = getClazz();
112        ReadBuffer buf = cl.getReadBuffer();
113        int idSize = cl.getIdentifierSize();
114        long offset = getOffset() + idSize + 4;
115        // length of the array
116        int length = buf.getInt(offset);
117        // typecode of array element type
118        byte type = buf.getByte(offset + 4);
119        if (length == 0) {
120            return Snapshot.EMPTY_BYTE_ARRAY;
121        } else {
122            length *= elementSize(type);
123            byte[] res = new byte[length];
124            buf.get(offset + 5, res);
125            return res;
126        }
127    }
128
129    // JavaClass set only after resolve.
130    private JavaClass clazz;
131
132    // This field contains elementSignature byte and
133    // divider to be used to calculate length. Note that
134    // length of content byte[] is not same as array length.
135    // Actual array length is (byte[].length / divider)
136    private int data;
137
138    // First 8 bits of data is used for element signature
139    private static final int SIGNATURE_MASK = 0x0FF;
140
141    // Next 8 bits of data is used for length divider
142    private static final int LENGTH_DIVIDER_MASK = 0x0FF00;
143
144    // Number of bits to shift to get length divider
145    private static final int LENGTH_DIVIDER_SHIFT = 8;
146
147    public JavaValueArray(byte elementSignature, long offset) {
148        super(offset);
149        this.data = (elementSignature & SIGNATURE_MASK);
150    }
151
152    public JavaClass getClazz() {
153        return clazz;
154    }
155
156    public void visitReferencedObjects(JavaHeapObjectVisitor v) {
157        super.visitReferencedObjects(v);
158    }
159
160    public void resolve(Snapshot snapshot) {
161        if (clazz instanceof JavaClass) {
162            return;
163        }
164        byte elementSig = getElementType();
165        clazz = snapshot.findClass(arrayTypeName(elementSig));
166        if (clazz == null) {
167            clazz = snapshot.getArrayClass("" + ((char) elementSig));
168        }
169        getClazz().addInstance(this);
170        super.resolve(snapshot);
171    }
172
173    public int getLength() {
174        int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT;
175        if (divider == 0) {
176            byte elementSignature = getElementType();
177            switch (elementSignature) {
178            case 'B':
179            case 'Z':
180                divider = 1;
181                break;
182            case 'C':
183            case 'S':
184                divider = 2;
185                break;
186            case 'I':
187            case 'F':
188                divider = 4;
189                break;
190            case 'J':
191            case 'D':
192                divider = 8;
193                break;
194            default:
195                throw new RuntimeException("unknown primitive type: " +
196                                elementSignature);
197            }
198            data |= (divider << LENGTH_DIVIDER_SHIFT);
199        }
200        return (getValueLength() / divider);
201    }
202
203    public Object getElements() {
204        final int len = getLength();
205        final byte et = getElementType();
206        byte[] data = getValue();
207        int index = 0;
208        switch (et) {
209            case 'Z': {
210                boolean[] res = new boolean[len];
211                for (int i = 0; i < len; i++) {
212                    res[i] = booleanAt(index, data);
213                    index++;
214                }
215                return res;
216            }
217            case 'B': {
218                byte[] res = new byte[len];
219                for (int i = 0; i < len; i++) {
220                    res[i] = byteAt(index, data);
221                    index++;
222                }
223                return res;
224            }
225            case 'C': {
226                char[] res = new char[len];
227                for (int i = 0; i < len; i++) {
228                    res[i] = charAt(index, data);
229                    index += 2;
230                }
231                return res;
232            }
233            case 'S': {
234                short[] res = new short[len];
235                for (int i = 0; i < len; i++) {
236                    res[i] = shortAt(index, data);
237                    index += 2;
238                }
239                return res;
240            }
241            case 'I': {
242                int[] res = new int[len];
243                for (int i = 0; i < len; i++) {
244                    res[i] = intAt(index, data);
245                    index += 4;
246                }
247                return res;
248            }
249            case 'J': {
250                long[] res = new long[len];
251                for (int i = 0; i < len; i++) {
252                    res[i] = longAt(index, data);
253                    index += 8;
254                }
255                return res;
256            }
257            case 'F': {
258                float[] res = new float[len];
259                for (int i = 0; i < len; i++) {
260                    res[i] = floatAt(index, data);
261                    index += 4;
262                }
263                return res;
264            }
265            case 'D': {
266                double[] res = new double[len];
267                for (int i = 0; i < len; i++) {
268                    res[i] = doubleAt(index, data);
269                    index += 8;
270                }
271                return res;
272            }
273            default: {
274                throw new RuntimeException("unknown primitive type?");
275            }
276        }
277    }
278
279    public byte getElementType() {
280        return (byte) (data & SIGNATURE_MASK);
281    }
282
283    private void checkIndex(int index) {
284        if (index < 0 || index >= getLength()) {
285            throw new ArrayIndexOutOfBoundsException(index);
286        }
287    }
288
289    private void requireType(char type) {
290        if (getElementType() != type) {
291            throw new RuntimeException("not of type : " + type);
292        }
293    }
294
295    public boolean getBooleanAt(int index) {
296        checkIndex(index);
297        requireType('Z');
298        return booleanAt(index, getValue());
299    }
300
301    public byte getByteAt(int index) {
302        checkIndex(index);
303        requireType('B');
304        return byteAt(index, getValue());
305    }
306
307    public char getCharAt(int index) {
308        checkIndex(index);
309        requireType('C');
310        return charAt(index << 1, getValue());
311    }
312
313    public short getShortAt(int index) {
314        checkIndex(index);
315        requireType('S');
316        return shortAt(index << 1, getValue());
317    }
318
319    public int getIntAt(int index) {
320        checkIndex(index);
321        requireType('I');
322        return intAt(index << 2, getValue());
323    }
324
325    public long getLongAt(int index) {
326        checkIndex(index);
327        requireType('J');
328        return longAt(index << 3, getValue());
329    }
330
331    public float getFloatAt(int index) {
332        checkIndex(index);
333        requireType('F');
334        return floatAt(index << 2, getValue());
335    }
336
337    public double getDoubleAt(int index) {
338        checkIndex(index);
339        requireType('D');
340        return doubleAt(index << 3, getValue());
341    }
342
343    public String valueString() {
344        return valueString(true);
345    }
346
347    public String valueString(boolean bigLimit) {
348        // Char arrays deserve special treatment
349        StringBuilder result;
350        byte[] value = getValue();
351        int max = value.length;
352        byte elementSignature = getElementType();
353        if (elementSignature == 'C')  {
354            result = new StringBuilder();
355            for (int i = 0; i < value.length; ) {
356                char val = charAt(i, value);
357                result.append(val);
358                i += 2;
359            }
360        } else {
361            int limit = 8;
362            if (bigLimit) {
363                limit = 1000;
364            }
365            result = new StringBuilder("{");
366            int num = 0;
367            for (int i = 0; i < value.length; ) {
368                if (num > 0) {
369                    result.append(", ");
370                }
371                if (num >= limit) {
372                    result.append("... ");
373                    break;
374                }
375                num++;
376                switch (elementSignature) {
377                    case 'Z': {
378                        boolean val = booleanAt(i, value);
379                        if (val) {
380                            result.append("true");
381                        } else {
382                            result.append("false");
383                        }
384                        i++;
385                        break;
386                    }
387                    case 'B': {
388                        int val = 0xFF & byteAt(i, value);
389                        result.append("0x").append(Integer.toString(val, 16));
390                        i++;
391                        break;
392                    }
393                    case 'S': {
394                        short val = shortAt(i, value);
395                        i += 2;
396                        result.append(val);
397                        break;
398                    }
399                    case 'I': {
400                        int val = intAt(i, value);
401                        i += 4;
402                        result.append(val);
403                        break;
404                    }
405                    case 'J': {         // long
406                        long val = longAt(i, value);
407                        result.append(val);
408                        i += 8;
409                        break;
410                    }
411                    case 'F': {
412                        float val = floatAt(i, value);
413                        result.append(val);
414                        i += 4;
415                        break;
416                    }
417                    case 'D': {         // double
418                        double val = doubleAt(i, value);
419                        result.append(val);
420                        i += 8;
421                        break;
422                    }
423                    default: {
424                        throw new RuntimeException("unknown primitive type?");
425                    }
426                }
427            }
428            result.append('}');
429        }
430        return result.toString();
431    }
432
433}
434