1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib.Util;
30
31/**
32 * Utilities for formatting numbers as hexadecimal.
33 */
34public final class Hex {
35    /**
36     * This class is uninstantiable.
37     */
38    private Hex() {
39        // This space intentionally left blank.
40    }
41
42    /**
43     * Formats a <code>long</code> as an 8-byte unsigned hex value.
44     *
45     * @param v value to format
46     * @return non-null; formatted form
47     */
48    public static String u8(long v) {
49        char[] result = new char[16];
50        for (int i = 0; i < 16; i++) {
51            result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
52            v >>= 4;
53        }
54
55        return new String(result);
56    }
57
58    /**
59     * Formats an <code>int</code> as a 4-byte unsigned hex value.
60     *
61     * @param v value to format
62     * @return non-null; formatted form
63     */
64    public static String u4(int v) {
65        char[] result = new char[8];
66        for (int i = 0; i < 8; i++) {
67            result[7 - i] = Character.forDigit(v & 0x0f, 16);
68            v >>= 4;
69        }
70
71        return new String(result);
72    }
73
74    /**
75     * Formats an <code>int</code> as a 3-byte unsigned hex value.
76     *
77     * @param v value to format
78     * @return non-null; formatted form
79     */
80    public static String u3(int v) {
81        char[] result = new char[6];
82        for (int i = 0; i < 6; i++) {
83            result[5 - i] = Character.forDigit(v & 0x0f, 16);
84            v >>= 4;
85        }
86
87        return new String(result);
88    }
89
90    /**
91     * Formats an <code>int</code> as a 2-byte unsigned hex value.
92     *
93     * @param v value to format
94     * @return non-null; formatted form
95     */
96    public static String u2(int v) {
97        char[] result = new char[4];
98        for (int i = 0; i < 4; i++) {
99            result[3 - i] = Character.forDigit(v & 0x0f, 16);
100            v >>= 4;
101        }
102
103        return new String(result);
104    }
105
106    /**
107     * Formats an <code>int</code> as either a 2-byte unsigned hex value
108     * (if the value is small enough) or a 4-byte unsigned hex value (if
109     * not).
110     *
111     * @param v value to format
112     * @return non-null; formatted form
113     */
114    public static String u2or4(int v) {
115        if (v == (char) v) {
116            return u2(v);
117        } else {
118            return u4(v);
119        }
120    }
121
122    /**
123     * Formats an <code>int</code> as a 1-byte unsigned hex value.
124     *
125     * @param v value to format
126     * @return non-null; formatted form
127     */
128    public static String u1(int v) {
129        char[] result = new char[2];
130        for (int i = 0; i < 2; i++) {
131            result[1 - i] = Character.forDigit(v & 0x0f, 16);
132            v >>= 4;
133        }
134
135        return new String(result);
136    }
137
138    /**
139     * Formats an <code>int</code> as a 4-bit unsigned hex nibble.
140     *
141     * @param v value to format
142     * @return non-null; formatted form
143     */
144    public static String uNibble(int v) {
145        char[] result = new char[1];
146
147        result[0] = Character.forDigit(v & 0x0f, 16);
148        return new String(result);
149    }
150
151    /**
152     * Formats a <code>long</code> as an 8-byte signed hex value.
153     *
154     * @param v value to format
155     * @return non-null; formatted form
156     */
157    public static String s8(long v) {
158        char[] result = new char[17];
159
160        if (v < 0) {
161            result[0] = '-';
162            v = -v;
163        } else {
164            result[0] = '+';
165        }
166
167        for (int i = 0; i < 16; i++) {
168            result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
169            v >>= 4;
170        }
171
172        return new String(result);
173    }
174
175    /**
176     * Formats an <code>int</code> as a 4-byte signed hex value.
177     *
178     * @param v value to format
179     * @return non-null; formatted form
180     */
181    public static String s4(int v) {
182        char[] result = new char[9];
183
184        if (v < 0) {
185            result[0] = '-';
186            v = -v;
187        } else {
188            result[0] = '+';
189        }
190
191        for (int i = 0; i < 8; i++) {
192            result[8 - i] = Character.forDigit(v & 0x0f, 16);
193            v >>= 4;
194        }
195
196        return new String(result);
197    }
198
199    /**
200     * Formats an <code>int</code> as a 2-byte signed hex value.
201     *
202     * @param v value to format
203     * @return non-null; formatted form
204     */
205    public static String s2(int v) {
206        char[] result = new char[5];
207
208        if (v < 0) {
209            result[0] = '-';
210            v = -v;
211        } else {
212            result[0] = '+';
213        }
214
215        for (int i = 0; i < 4; i++) {
216            result[4 - i] = Character.forDigit(v & 0x0f, 16);
217            v >>= 4;
218        }
219
220        return new String(result);
221    }
222
223    /**
224     * Formats an <code>int</code> as a 1-byte signed hex value.
225     *
226     * @param v value to format
227     * @return non-null; formatted form
228     */
229    public static String s1(int v) {
230        char[] result = new char[3];
231
232        if (v < 0) {
233            result[0] = '-';
234            v = -v;
235        } else {
236            result[0] = '+';
237        }
238
239        for (int i = 0; i < 2; i++) {
240            result[2 - i] = Character.forDigit(v & 0x0f, 16);
241            v >>= 4;
242        }
243
244        return new String(result);
245    }
246
247    /**
248     * Formats a hex dump of a portion of a <code>byte[]</code>. The result
249     * is always newline-terminated, unless the passed-in length was zero,
250     * in which case the result is always the empty string (<code>""</code>).
251     *
252     * @param arr non-null; array to format
253     * @param offset &gt;= 0; offset to the part to dump
254     * @param length &gt;= 0; number of bytes to dump
255     * @param outOffset &gt;= 0; first output offset to print
256     * @param bpl &gt;= 0; number of bytes of output per line
257     * @param addressLength {2,4,6,8}; number of characters for each address
258     * header
259     * @return non-null; a string of the dump
260     */
261    public static String dump(byte[] arr, int offset, int length,
262                              int outOffset, int bpl, int addressLength) {
263        int end = offset + length;
264
265        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
266        if (((offset | length | end) < 0) || (end > arr.length)) {
267            throw new IndexOutOfBoundsException("arr.length " +
268                                                arr.length + "; " +
269                                                offset + "..!" + end);
270        }
271
272        if (outOffset < 0) {
273            throw new IllegalArgumentException("outOffset < 0");
274        }
275
276        if (length == 0) {
277            return "";
278        }
279
280        StringBuffer sb = new StringBuffer(length * 4 + 6);
281        boolean bol = true;
282        int col = 0;
283
284        while (length > 0) {
285            if (col == 0) {
286                String astr;
287                switch (addressLength) {
288                    case 2:  astr = Hex.u1(outOffset); break;
289                    case 4:  astr = Hex.u2(outOffset); break;
290                    case 6:  astr = Hex.u3(outOffset); break;
291                    default: astr = Hex.u4(outOffset); break;
292                }
293                sb.append(astr);
294                sb.append(": ");
295            } else if ((col & 1) == 0) {
296                sb.append(' ');
297            }
298            sb.append(Hex.u1(arr[offset]));
299            outOffset++;
300            offset++;
301            col++;
302            if (col == bpl) {
303                sb.append('\n');
304                col = 0;
305            }
306            length--;
307        }
308
309        if (col != 0) {
310            sb.append('\n');
311        }
312
313        return sb.toString();
314    }
315}