HexParser.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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 parsing hexadecimal text.
21 */
22public final class HexParser {
23    /**
24     * This class is uninstantiable.
25     */
26    private HexParser() {
27        // This space intentionally left blank.
28    }
29
30    /**
31     * Parses the given text as hex, returning a {@code byte[]}
32     * corresponding to the text. The format is simple: Each line may
33     * start with a hex offset followed by a colon (which is verified
34     * and presumably used just as a comment), and then consists of
35     * hex digits freely interspersed with whitespace. If a pound sign
36     * is encountered, it and the rest of the line are ignored as a
37     * comment. If a double quote is encountered, then the ASCII value
38     * of the subsequent characters is used, until the next double
39     * quote. Quoted strings may not span multiple lines.
40     *
41     * @param src {@code non-null;} the source string
42     * @return {@code non-null;} the parsed form
43     */
44    public static byte[] parse(String src) {
45        int len = src.length();
46        byte[] result = new byte[len / 2];
47        int at = 0;
48        int outAt = 0;
49
50        while (at < len) {
51            int nlAt = src.indexOf('\n', at);
52            if (nlAt < 0) {
53                nlAt = len;
54            }
55            int poundAt = src.indexOf('#', at);
56
57            String line;
58            if ((poundAt >= 0) && (poundAt < nlAt)) {
59                line = src.substring(at, poundAt);
60            } else {
61                line = src.substring(at, nlAt);
62            }
63            at = nlAt + 1;
64
65            int colonAt = line.indexOf(':');
66
67            atCheck:
68            if (colonAt != -1) {
69                int quoteAt = line.indexOf('\"');
70                if ((quoteAt != -1) && (quoteAt < colonAt)) {
71                    break atCheck;
72                }
73
74                String atStr = line.substring(0, colonAt).trim();
75                line = line.substring(colonAt + 1);
76                int alleged = Integer.parseInt(atStr, 16);
77                if (alleged != outAt) {
78                    throw new RuntimeException("bogus offset marker: " +
79                                               atStr);
80                }
81            }
82
83            int lineLen = line.length();
84            int value = -1;
85            boolean quoteMode = false;
86
87            for (int i = 0; i < lineLen; i++) {
88                char c = line.charAt(i);
89
90                if (quoteMode) {
91                    if (c == '\"') {
92                        quoteMode = false;
93                    } else {
94                        result[outAt] = (byte) c;
95                        outAt++;
96                    }
97                    continue;
98                }
99
100                if (c <= ' ') {
101                    continue;
102                }
103                if (c == '\"') {
104                    if (value != -1) {
105                        throw new RuntimeException("spare digit around " +
106                                                   "offset " + Hex.u4(outAt));
107                    }
108                    quoteMode = true;
109                    continue;
110                }
111
112                int digVal = Character.digit(c, 16);
113                if (digVal == -1) {
114                    throw new RuntimeException("bogus digit character: \"" +
115                                               c + "\"");
116                }
117                if (value == -1) {
118                    value = digVal;
119                } else {
120                    result[outAt] = (byte) ((value << 4) | digVal);
121                    outAt++;
122                    value = -1;
123                }
124            }
125
126            if (value != -1) {
127                throw new RuntimeException("spare digit around offset " +
128                                           Hex.u4(outAt));
129            }
130
131            if (quoteMode) {
132                throw new RuntimeException("unterminated quote around " +
133                                           "offset " + Hex.u4(outAt));
134            }
135        }
136
137        if (outAt < result.length) {
138            byte[] newr = new byte[outAt];
139            System.arraycopy(result, 0, newr, 0, outAt);
140            result = newr;
141        }
142
143        return result;
144    }
145}
146