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.smali;
30
31import java.util.regex.Matcher;
32import java.util.regex.Pattern;
33
34public class LiteralTools
35{
36    public static byte parseByte(String byteLiteral)
37            throws NumberFormatException {
38        if (byteLiteral == null) {
39            throw new NumberFormatException("string is null");
40        }
41        if (byteLiteral.length() == 0) {
42            throw new NumberFormatException("string is blank");
43        }
44
45        char[] byteChars;
46        if (byteLiteral.toUpperCase().endsWith("T")) {
47            byteChars = byteLiteral.substring(0, byteLiteral.length()-1).toCharArray();
48        } else {
49            byteChars = byteLiteral.toCharArray();
50        }
51
52        int position = 0;
53        int radix = 10;
54        boolean negative = false;
55        if (byteChars[position] == '-') {
56            position++;
57            negative = true;
58        }
59
60        if (byteChars[position] == '0') {
61            position++;
62            if (position == byteChars.length) {
63                return 0;
64            } else if (byteChars[position] == 'x' || byteChars[position] == 'X') {
65                radix = 16;
66                position++;
67            } else if (Character.digit(byteChars[position], 8) >= 0) {
68                radix = 8;
69            }
70        }
71
72        byte result = 0;
73        byte shiftedResult;
74        int digit;
75        byte maxValue = (byte)(Byte.MAX_VALUE / (radix / 2));
76
77        while (position < byteChars.length) {
78            digit = Character.digit(byteChars[position], radix);
79            if (digit < 0) {
80                throw new NumberFormatException("The string contains invalid an digit - '" + byteChars[position] + "'");
81            }
82            shiftedResult = (byte)(result * radix);
83            if (result > maxValue) {
84                throw new NumberFormatException(byteLiteral + " cannot fit into a byte");
85            }
86            if (shiftedResult < 0 && shiftedResult >= -digit) {
87                throw new NumberFormatException(byteLiteral + " cannot fit into a byte");
88            }
89            result = (byte)(shiftedResult + digit);
90            position++;
91        }
92
93        if (negative) {
94            //allow -0x80, which is = 0x80
95            if (result == Byte.MIN_VALUE) {
96                return result;
97            } else if (result < 0) {
98                throw new NumberFormatException(byteLiteral + " cannot fit into a byte");
99            }
100            return (byte)(result * -1);
101        } else {
102            return result;
103        }
104    }
105
106    public static short parseShort(String shortLiteral)
107            throws NumberFormatException {
108        if (shortLiteral == null) {
109            throw new NumberFormatException("string is null");
110        }
111        if (shortLiteral.length() == 0) {
112            throw new NumberFormatException("string is blank");
113        }
114
115        char[] shortChars;
116        if (shortLiteral.toUpperCase().endsWith("S")) {
117            shortChars = shortLiteral.substring(0, shortLiteral.length()-1).toCharArray();
118        } else {
119            shortChars = shortLiteral.toCharArray();
120        }
121
122        int position = 0;
123        int radix = 10;
124        boolean negative = false;
125        if (shortChars[position] == '-') {
126            position++;
127            negative = true;
128        }
129
130        if (shortChars[position] == '0') {
131            position++;
132            if (position == shortChars.length) {
133                return 0;
134            } else if (shortChars[position] == 'x' || shortChars[position] == 'X') {
135                radix = 16;
136                position++;
137            } else if (Character.digit(shortChars[position], 8) >= 0) {
138                radix = 8;
139            }
140        }
141
142        short result = 0;
143        short shiftedResult;
144        int digit;
145        short maxValue = (short)(Short.MAX_VALUE / (radix / 2));
146
147        while (position < shortChars.length) {
148            digit = Character.digit(shortChars[position], radix);
149            if (digit < 0) {
150                throw new NumberFormatException("The string contains invalid an digit - '" + shortChars[position] + "'");
151            }
152            shiftedResult = (short)(result * radix);
153            if (result > maxValue) {
154                throw new NumberFormatException(shortLiteral + " cannot fit into a short");
155            }
156            if (shiftedResult < 0 && shiftedResult >= -digit) {
157                throw new NumberFormatException(shortLiteral + " cannot fit into a short");
158            }
159            result = (short)(shiftedResult + digit);
160            position++;
161        }
162
163        if (negative) {
164            //allow -0x8000, which is = 0x8000
165            if (result == Short.MIN_VALUE) {
166                return result;
167            } else if (result < 0) {
168                throw new NumberFormatException(shortLiteral + " cannot fit into a short");
169            }
170            return (short)(result * -1);
171        } else {
172            return result;
173        }
174    }
175
176    public static int parseInt(String intLiteral)
177            throws NumberFormatException {
178        if (intLiteral == null) {
179            throw new NumberFormatException("string is null");
180        }
181        if (intLiteral.length() == 0) {
182            throw new NumberFormatException("string is blank");
183        }
184
185        char[] intChars = intLiteral.toCharArray();
186        int position = 0;
187        int radix = 10;
188        boolean negative = false;
189        if (intChars[position] == '-') {
190            position++;
191            negative = true;
192        }
193
194        if (intChars[position] == '0') {
195            position++;
196            if (position == intChars.length) {
197                return 0;
198            } else if (intChars[position] == 'x' || intChars[position] == 'X') {
199                radix = 16;
200                position++;
201            } else if (Character.digit(intChars[position], 8) >= 0) {
202                radix = 8;
203            }
204        }
205
206        int result = 0;
207        int shiftedResult;
208        int digit;
209        int maxValue = Integer.MAX_VALUE / (radix / 2);
210
211        while (position < intChars.length) {
212            digit = Character.digit(intChars[position], radix);
213            if (digit < 0) {
214                throw new NumberFormatException("The string contains an invalid digit - '" + intChars[position] + "'");
215            }
216            shiftedResult = result * radix;
217            if (result > maxValue) {
218                throw new NumberFormatException(intLiteral + " cannot fit into an int");
219            }
220            if (shiftedResult < 0 && shiftedResult >= -digit) {
221                throw new NumberFormatException(intLiteral + " cannot fit into an int");
222            }
223            result = shiftedResult + digit;
224            position++;
225        }
226
227        if (negative) {
228            //allow -0x80000000, which is = 0x80000000
229            if (result == Integer.MIN_VALUE) {
230                return result;
231            } else if (result < 0) {
232                throw new NumberFormatException(intLiteral + " cannot fit into an int");
233            }
234            return result * -1;
235        } else {
236            return result;
237        }
238    }
239
240    public static long parseLong(String longLiteral)
241            throws NumberFormatException {
242        if (longLiteral == null) {
243            throw new NumberFormatException("string is null");
244        }
245        if (longLiteral.length() == 0) {
246            throw new NumberFormatException("string is blank");
247        }
248
249        char[] longChars;
250        if (longLiteral.toUpperCase().endsWith("L")) {
251            longChars = longLiteral.substring(0, longLiteral.length()-1).toCharArray();
252        } else {
253            longChars = longLiteral.toCharArray();
254        }
255
256        int position = 0;
257        int radix = 10;
258        boolean negative = false;
259        if (longChars[position] == '-') {
260            position++;
261            negative = true;
262        }
263
264        if (longChars[position] == '0') {
265            position++;
266            if (position == longChars.length) {
267                return 0;
268            } else if (longChars[position] == 'x' || longChars[position] == 'X') {
269                radix = 16;
270                position++;
271            } else if (Character.digit(longChars[position], 8) >= 0) {
272                radix = 8;
273            }
274        }
275
276        long result = 0;
277        long shiftedResult;
278        int digit;
279        long maxValue = Long.MAX_VALUE / (radix / 2);
280
281        while (position < longChars.length) {
282            digit = Character.digit(longChars[position], radix);
283            if (digit < 0) {
284                throw new NumberFormatException("The string contains an invalid digit - '" + longChars[position] + "'");
285            }
286            shiftedResult = result * radix;
287            if (result > maxValue) {
288                throw new NumberFormatException(longLiteral + " cannot fit into a long");
289            }
290            if (shiftedResult < 0 && shiftedResult >= -digit) {
291                throw new NumberFormatException(longLiteral + " cannot fit into a long");
292            }
293            result = shiftedResult + digit;
294            position++;
295        }
296
297        if (negative) {
298            //allow -0x8000000000000000, which is = 0x8000000000000000
299            if (result == Long.MIN_VALUE) {
300                return result;
301            } else if (result < 0) {
302                throw new NumberFormatException(longLiteral + " cannot fit into a long");
303            }
304            return result * -1;
305        } else {
306            return result;
307        }
308    }
309
310    private static Pattern specialFloatRegex = Pattern.compile("((-)?infinityf)|(nanf)", Pattern.CASE_INSENSITIVE);
311    public static float parseFloat(String floatString) {
312        Matcher m = specialFloatRegex.matcher(floatString);
313        if (m.matches()) {
314            //got an infinity
315            if (m.start(1) != -1) {
316                if (m.start(2) != -1) {
317                    return Float.NEGATIVE_INFINITY;
318                } else {
319                    return Float.POSITIVE_INFINITY;
320                }
321            } else {
322                return Float.NaN;
323            }
324        }
325        return Float.parseFloat(floatString);
326    }
327
328    private static Pattern specialDoubleRegex = Pattern.compile("((-)?infinityd?)|(nand?)", Pattern.CASE_INSENSITIVE);
329    public static double parseDouble(String doubleString) {
330        Matcher m = specialDoubleRegex.matcher(doubleString);
331        if (m.matches()) {
332            //got an infinity
333            if (m.start(1) != -1) {
334                if (m.start(2) != -1) {
335                    return Double.NEGATIVE_INFINITY;
336                } else {
337                    return Double.POSITIVE_INFINITY;
338                }
339            } else {
340                return Double.NaN;
341            }
342        }
343        return Double.parseDouble(doubleString);
344    }
345
346    public static byte[] longToBytes(long value) {
347        byte[] bytes = new byte[8];
348
349        for (int i=0; value != 0; i++) {
350            bytes[i] = (byte)value;
351            value = value >>> 8;
352        }
353        return bytes;
354    }
355
356    public static byte[] intToBytes(int value) {
357        byte[] bytes = new byte[4];
358
359        for (int i=0; value != 0; i++) {
360            bytes[i] = (byte)value;
361            value = value >>> 8;
362        }
363        return bytes;
364    }
365
366    public static byte[] shortToBytes(short value) {
367        byte[] bytes = new byte[2];
368
369        bytes[0] = (byte)value;
370        bytes[1] = (byte)(value >>> 8);
371        return bytes;
372    }
373
374    public static byte[] floatToBytes(float value) {
375        return intToBytes(Float.floatToRawIntBits(value));
376    }
377
378    public static byte[] doubleToBytes(double value) {
379        return longToBytes(Double.doubleToRawLongBits(value));
380    }
381
382    public static byte[] charToBytes(char value) {
383        return shortToBytes((short)value);
384    }
385
386    public static byte[] boolToBytes(boolean value) {
387        if (value) {
388            return new byte[] { 0x01 };
389        } else {
390            return new byte[] { 0x00 };
391        }
392    }
393
394    public static void checkInt(long value) {
395        if (value > 0xFFFFFFFF || value < -0x80000000) {
396            throw new NumberFormatException(Long.toString(value) + " cannot fit into an int");
397        }
398    }
399
400    public static void checkShort(long value) {
401        if (value > 0xFFFF | value < -0x8000) {
402            throw new NumberFormatException(Long.toString(value) + " cannot fit into a short");
403        }
404    }
405
406    public static void checkByte(long value) {
407        if (value > 0xFF | value < -0x80) {
408            throw new NumberFormatException(Long.toString(value) + " cannot fit into a byte");
409        }
410    }
411
412    public static void checkNibble(long value) {
413        if (value > 0x0F | value < -0x08) {
414            throw new NumberFormatException(Long.toString(value) + " cannot fit into a nibble");
415        }
416    }
417}
418