1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.util;
18
19/**
20 * Utilities for formatting numbers as hexadecimal.
21 */
22public final class Hex {
23    /**
24     * This class is uninstantiable.
25     */
26    private Hex() {
27        // This space intentionally left blank.
28    }
29
30    /**
31     * Formats a {@code long} as an 8-byte unsigned hex value.
32     *
33     * @param v value to format
34     * @return {@code non-null;} formatted form
35     */
36    public static String u8(long v) {
37        char[] result = new char[16];
38        for (int i = 0; i < 16; i++) {
39            result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
40            v >>= 4;
41        }
42
43        return new String(result);
44    }
45
46    /**
47     * Formats an {@code int} as a 4-byte unsigned hex value.
48     *
49     * @param v value to format
50     * @return {@code non-null;} formatted form
51     */
52    public static String u4(int v) {
53        char[] result = new char[8];
54        for (int i = 0; i < 8; i++) {
55            result[7 - i] = Character.forDigit(v & 0x0f, 16);
56            v >>= 4;
57        }
58
59        return new String(result);
60    }
61
62    /**
63     * Formats an {@code int} as a 3-byte unsigned hex value.
64     *
65     * @param v value to format
66     * @return {@code non-null;} formatted form
67     */
68    public static String u3(int v) {
69        char[] result = new char[6];
70        for (int i = 0; i < 6; i++) {
71            result[5 - i] = Character.forDigit(v & 0x0f, 16);
72            v >>= 4;
73        }
74
75        return new String(result);
76    }
77
78    /**
79     * Formats an {@code int} as a 2-byte unsigned hex value.
80     *
81     * @param v value to format
82     * @return {@code non-null;} formatted form
83     */
84    public static String u2(int v) {
85        char[] result = new char[4];
86        for (int i = 0; i < 4; i++) {
87            result[3 - i] = Character.forDigit(v & 0x0f, 16);
88            v >>= 4;
89        }
90
91        return new String(result);
92    }
93
94    /**
95     * Formats an {@code int} as either a 2-byte unsigned hex value
96     * (if the value is small enough) or a 4-byte unsigned hex value (if
97     * not).
98     *
99     * @param v value to format
100     * @return {@code non-null;} formatted form
101     */
102    public static String u2or4(int v) {
103        if (v == (char) v) {
104            return u2(v);
105        } else {
106            return u4(v);
107        }
108    }
109
110    /**
111     * Formats an {@code int} as a 1-byte unsigned hex value.
112     *
113     * @param v value to format
114     * @return {@code non-null;} formatted form
115     */
116    public static String u1(int v) {
117        char[] result = new char[2];
118        for (int i = 0; i < 2; i++) {
119            result[1 - i] = Character.forDigit(v & 0x0f, 16);
120            v >>= 4;
121        }
122
123        return new String(result);
124    }
125
126    /**
127     * Formats an {@code int} as a 4-bit unsigned hex nibble.
128     *
129     * @param v value to format
130     * @return {@code non-null;} formatted form
131     */
132    public static String uNibble(int v) {
133        char[] result = new char[1];
134
135        result[0] = Character.forDigit(v & 0x0f, 16);
136        return new String(result);
137    }
138
139    /**
140     * Formats a {@code long} as an 8-byte signed hex value.
141     *
142     * @param v value to format
143     * @return {@code non-null;} formatted form
144     */
145    public static String s8(long v) {
146        char[] result = new char[17];
147
148        if (v < 0) {
149            result[0] = '-';
150            v = -v;
151        } else {
152            result[0] = '+';
153        }
154
155        for (int i = 0; i < 16; i++) {
156            result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
157            v >>= 4;
158        }
159
160        return new String(result);
161    }
162
163    /**
164     * Formats an {@code int} as a 4-byte signed hex value.
165     *
166     * @param v value to format
167     * @return {@code non-null;} formatted form
168     */
169    public static String s4(int v) {
170        char[] result = new char[9];
171
172        if (v < 0) {
173            result[0] = '-';
174            v = -v;
175        } else {
176            result[0] = '+';
177        }
178
179        for (int i = 0; i < 8; i++) {
180            result[8 - i] = Character.forDigit(v & 0x0f, 16);
181            v >>= 4;
182        }
183
184        return new String(result);
185    }
186
187    /**
188     * Formats an {@code int} as a 2-byte signed hex value.
189     *
190     * @param v value to format
191     * @return {@code non-null;} formatted form
192     */
193    public static String s2(int v) {
194        char[] result = new char[5];
195
196        if (v < 0) {
197            result[0] = '-';
198            v = -v;
199        } else {
200            result[0] = '+';
201        }
202
203        for (int i = 0; i < 4; i++) {
204            result[4 - i] = Character.forDigit(v & 0x0f, 16);
205            v >>= 4;
206        }
207
208        return new String(result);
209    }
210
211    /**
212     * Formats an {@code int} as a 1-byte signed hex value.
213     *
214     * @param v value to format
215     * @return {@code non-null;} formatted form
216     */
217    public static String s1(int v) {
218        char[] result = new char[3];
219
220        if (v < 0) {
221            result[0] = '-';
222            v = -v;
223        } else {
224            result[0] = '+';
225        }
226
227        for (int i = 0; i < 2; i++) {
228            result[2 - i] = Character.forDigit(v & 0x0f, 16);
229            v >>= 4;
230        }
231
232        return new String(result);
233    }
234
235    /**
236     * Formats a hex dump of a portion of a {@code byte[]}. The result
237     * is always newline-terminated, unless the passed-in length was zero,
238     * in which case the result is always the empty string ({@code ""}).
239     *
240     * @param arr {@code non-null;} array to format
241     * @param offset {@code >= 0;} offset to the part to dump
242     * @param length {@code >= 0;} number of bytes to dump
243     * @param outOffset {@code >= 0;} first output offset to print
244     * @param bpl {@code >= 0;} number of bytes of output per line
245     * @param addressLength {@code {2,4,6,8};} number of characters for each address
246     * header
247     * @return {@code non-null;} a string of the dump
248     */
249    public static String dump(byte[] arr, int offset, int length,
250                              int outOffset, int bpl, int addressLength) {
251        int end = offset + length;
252
253        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
254        if (((offset | length | end) < 0) || (end > arr.length)) {
255            throw new IndexOutOfBoundsException("arr.length " +
256                                                arr.length + "; " +
257                                                offset + "..!" + end);
258        }
259
260        if (outOffset < 0) {
261            throw new IllegalArgumentException("outOffset < 0");
262        }
263
264        if (length == 0) {
265            return "";
266        }
267
268        StringBuffer sb = new StringBuffer(length * 4 + 6);
269        boolean bol = true;
270        int col = 0;
271
272        while (length > 0) {
273            if (col == 0) {
274                String astr;
275                switch (addressLength) {
276                    case 2:  astr = Hex.u1(outOffset); break;
277                    case 4:  astr = Hex.u2(outOffset); break;
278                    case 6:  astr = Hex.u3(outOffset); break;
279                    default: astr = Hex.u4(outOffset); break;
280                }
281                sb.append(astr);
282                sb.append(": ");
283            } else if ((col & 1) == 0) {
284                sb.append(' ');
285            }
286            sb.append(Hex.u1(arr[offset]));
287            outOffset++;
288            offset++;
289            col++;
290            if (col == bpl) {
291                sb.append('\n');
292                col = 0;
293            }
294            length--;
295        }
296
297        if (col != 0) {
298            sb.append('\n');
299        }
300
301        return sb.toString();
302    }
303}
304