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 
17 package com.android.dx.util;
18 
19 /**
20  * Utilities for parsing hexadecimal text.
21  */
22 public final class HexParser {
23     /**
24      * This class is uninstantiable.
25      */
HexParser()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      */
parse(String src)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