1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/*
2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2007 The Android Open Source Project
3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License.
6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at
7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and
14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License.
15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
17579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpackage com.android.dx.util;
18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
19579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/**
20579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Utilities for parsing hexadecimal text.
21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class HexParser {
23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * This class is uninstantiable.
25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private HexParser() {
27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // This space intentionally left blank.
28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Parses the given text as hex, returning a {@code byte[]}
32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * corresponding to the text. The format is simple: Each line may
33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * start with a hex offset followed by a colon (which is verified
34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * and presumably used just as a comment), and then consists of
35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * hex digits freely interspersed with whitespace. If a pound sign
36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * is encountered, it and the rest of the line are ignored as a
37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * comment. If a double quote is encountered, then the ASCII value
38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * of the subsequent characters is used, until the next double
39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * quote. Quoted strings may not span multiple lines.
40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param src {@code non-null;} the source string
42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code non-null;} the parsed form
43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static byte[] parse(String src) {
45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int len = src.length();
46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        byte[] result = new byte[len / 2];
47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int at = 0;
48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int outAt = 0;
49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        while (at < len) {
51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int nlAt = src.indexOf('\n', at);
52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (nlAt < 0) {
53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                nlAt = len;
54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int poundAt = src.indexOf('#', at);
56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            String line;
58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if ((poundAt >= 0) && (poundAt < nlAt)) {
59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                line = src.substring(at, poundAt);
60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                line = src.substring(at, nlAt);
62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            at = nlAt + 1;
64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int colonAt = line.indexOf(':');
66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            atCheck:
68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (colonAt != -1) {
69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                int quoteAt = line.indexOf('\"');
70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if ((quoteAt != -1) && (quoteAt < colonAt)) {
71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    break atCheck;
72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                String atStr = line.substring(0, colonAt).trim();
75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                line = line.substring(colonAt + 1);
76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                int alleged = Integer.parseInt(atStr, 16);
77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (alleged != outAt) {
78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    throw new RuntimeException("bogus offset marker: " +
79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                               atStr);
80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int lineLen = line.length();
84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int value = -1;
85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            boolean quoteMode = false;
86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < lineLen; i++) {
88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                char c = line.charAt(i);
89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (quoteMode) {
91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    if (c == '\"') {
92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        quoteMode = false;
93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    } else {
94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        result[outAt] = (byte) c;
95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        outAt++;
96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    }
97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    continue;
98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (c <= ' ') {
101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    continue;
102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (c == '\"') {
104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    if (value != -1) {
105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        throw new RuntimeException("spare digit around " +
106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                                   "offset " + Hex.u4(outAt));
107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    }
108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    quoteMode = true;
109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    continue;
110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                int digVal = Character.digit(c, 16);
113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (digVal == -1) {
114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    throw new RuntimeException("bogus digit character: \"" +
115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                               c + "\"");
116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (value == -1) {
118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    value = digVal;
119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                } else {
120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    result[outAt] = (byte) ((value << 4) | digVal);
121579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    outAt++;
122579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    value = -1;
123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (value != -1) {
127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                throw new RuntimeException("spare digit around offset " +
128579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                           Hex.u4(outAt));
129579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
130579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
131579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (quoteMode) {
132579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                throw new RuntimeException("unterminated quote around " +
133579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                           "offset " + Hex.u4(outAt));
134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (outAt < result.length) {
138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            byte[] newr = new byte[outAt];
139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            System.arraycopy(result, 0, newr, 0, outAt);
140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result = newr;
141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson}
146