blob: 1b343453d8e935df09e87e58c267923a70d45df5 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.util;
/**
* Utilities for parsing hexadecimal text.
*/
public final class HexParser {
/**
* This class is uninstantiable.
*/
private HexParser() {
// This space intentionally left blank.
}
/**
* Parses the given text as hex, returning a {@code byte[]}
* corresponding to the text. The format is simple: Each line may
* start with a hex offset followed by a colon (which is verified
* and presumably used just as a comment), and then consists of
* hex digits freely interspersed with whitespace. If a pound sign
* is encountered, it and the rest of the line are ignored as a
* comment. If a double quote is encountered, then the ASCII value
* of the subsequent characters is used, until the next double
* quote. Quoted strings may not span multiple lines.
*
* @param src {@code non-null;} the source string
* @return {@code non-null;} the parsed form
*/
public static byte[] parse(String src) {
int len = src.length();
byte[] result = new byte[len / 2];
int at = 0;
int outAt = 0;
while (at < len) {
int nlAt = src.indexOf('\n', at);
if (nlAt < 0) {
nlAt = len;
}
int poundAt = src.indexOf('#', at);
String line;
if ((poundAt >= 0) && (poundAt < nlAt)) {
line = src.substring(at, poundAt);
} else {
line = src.substring(at, nlAt);
}
at = nlAt + 1;
int colonAt = line.indexOf(':');
atCheck:
if (colonAt != -1) {
int quoteAt = line.indexOf('\"');
if ((quoteAt != -1) && (quoteAt < colonAt)) {
break atCheck;
}
String atStr = line.substring(0, colonAt).trim();
line = line.substring(colonAt + 1);
int alleged = Integer.parseInt(atStr, 16);
if (alleged != outAt) {
throw new RuntimeException("bogus offset marker: " +
atStr);
}
}
int lineLen = line.length();
int value = -1;
boolean quoteMode = false;
for (int i = 0; i < lineLen; i++) {
char c = line.charAt(i);
if (quoteMode) {
if (c == '\"') {
quoteMode = false;
} else {
result[outAt] = (byte) c;
outAt++;
}
continue;
}
if (c <= ' ') {
continue;
}
if (c == '\"') {
if (value != -1) {
throw new RuntimeException("spare digit around " +
"offset " + Hex.u4(outAt));
}
quoteMode = true;
continue;
}
int digVal = Character.digit(c, 16);
if (digVal == -1) {
throw new RuntimeException("bogus digit character: \"" +
c + "\"");
}
if (value == -1) {
value = digVal;
} else {
result[outAt] = (byte) ((value << 4) | digVal);
outAt++;
value = -1;
}
}
if (value != -1) {
throw new RuntimeException("spare digit around offset " +
Hex.u4(outAt));
}
if (quoteMode) {
throw new RuntimeException("unterminated quote around " +
"offset " + Hex.u4(outAt));
}
}
if (outAt < result.length) {
byte[] newr = new byte[outAt];
System.arraycopy(result, 0, newr, 0, outAt);
result = newr;
}
return result;
}
}