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 */ 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