blob: 80e55a89f4635c0f387ff40e5f931a3065bfdc73 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2014 Google, Inc and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import java.util.Arrays;
import java.util.Comparator;
import org.eclipse.cdt.core.parser.ISignificantMacros;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
/**
* A set of static methods to encode Map<String, String> as an array of characters and to decode
* it back.
*
* The map is encoded as:
* {@code <number_of_entries>,<key1><value1>...<keyN><valueN>}.
* <p>
* Each string is encoded as: {@code <number_of_characters>,<characters>}.
* A {@code null} string is encoded as a single comma.
*/
public class SignificantMacros implements ISignificantMacros {
public static final char[] DEFINED = { 0 };
public static final char[] UNDEFINED = { 1 };
private static final Comparator<char[]> SORTER = new Comparator<>() {
@Override
public int compare(char[] s1, char[] s2) {
return CharArrayUtils.compare(s1, s2);
}
};
private final char[] fEncoded;
private int fHash;
public SignificantMacros(char[] encoded) {
assert encoded != null;
fEncoded = encoded;
}
public SignificantMacros(CharArrayObjectMap<char[]> sigMacros) {
fEncoded = encode(sigMacros);
}
private char[] encode(CharArrayObjectMap<char[]> sigMacros) {
StringBuilder buffer = new StringBuilder();
char[][] keys = sigMacros.keys();
Arrays.sort(keys, SORTER);
for (char[] name : keys) {
char[] value = sigMacros.get(name);
buffer.append((char) name.length).append(name);
buffer.append((char) value.length).append(value);
}
int len = buffer.length();
char[] result = new char[len];
buffer.getChars(0, len, result, 0);
return result;
}
@Override
public int hashCode() {
int h = fHash;
if (h == 0) {
char val[] = fEncoded;
int len = fEncoded.length;
for (int i = 0; i < len; i++) {
h = 31 * h + val[i];
}
fHash = h;
}
return h;
}
@Override
public boolean equals(Object obj) {
return obj instanceof SignificantMacros && hashCode() == obj.hashCode()
&& CharArrayUtils.equals(fEncoded, ((SignificantMacros) obj).fEncoded);
}
@Override
public boolean accept(IVisitor visitor) {
final char[] encoded = fEncoded;
final int len = encoded.length;
int i = 0;
while (i < len) {
final int len1 = encoded[i++];
int v = i + len1;
if (v >= len)
break;
final int len2 = encoded[v++];
if (v + len2 > len)
break;
char[] macro = extract(encoded, i, len1);
i = v + len2;
if (len2 == 1) {
if (encoded[v] == UNDEFINED[0]) {
if (!visitor.visitUndefined(macro))
return false;
continue;
}
if (encoded[v] == DEFINED[0]) {
if (!visitor.visitDefined(macro))
return false;
continue;
}
}
final char[] value = extract(encoded, v, len2);
if (!visitor.visitValue(macro, value))
return false;
}
return true;
}
public char[] extract(final char[] source, int from, final int length) {
char[] value = new char[length];
System.arraycopy(source, from, value, 0, length);
return value;
}
@Override
public char[] encode() {
return fEncoded;
}
/**
* For debugging purposes.
*/
@SuppressWarnings("nls")
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append('{');
accept(new IVisitor() {
@Override
public boolean visitValue(char[] macro, char[] value) {
buf.append(macro).append('=').append(value).append(',');
return true;
}
@Override
public boolean visitUndefined(char[] macro) {
buf.append(macro).append('=').append("null,");
return true;
}
@Override
public boolean visitDefined(char[] macro) {
buf.append(macro).append('=').append("*,");
return true;
}
});
int buflen = buf.length();
if (buflen > 1)
buf.setLength(buflen - 1);
buf.append('}');
return buf.toString();
}
public static char[] shortenValue(char[] expansion) {
if (expansion.length <= 16)
return expansion;
char[] result = new char[16];
System.arraycopy(expansion, 0, result, 0, 8);
StreamHasher hasher = new StreamHasher();
hasher.addChunk(expansion);
long hash = hasher.computeHash();
for (int i = 0; i < 8; i++) {
result[8 + i] = (char) (hash & 0xff);
hash = hash >> 1;
}
return result;
}
}